Bug 669040 part 1: Move mozilla/db/ to comm-central/db/ r=Standard8
authorMatheus Kerschbaum <matjk7@gmail.com>
Mon, 25 Jul 2011 23:00:00 -0700
changeset 8208 52efa9789800829c6f0ee6a005f83ed45a250396
parent 8207 abfd23d7c5042bc87502506c9f34c965fb9a09d1
child 8209 1dbfc673bc3f4b223a1a418e72d17ebdc7a48cfb
push id6313
push userPidgeot18@gmail.com
push dateFri, 29 Jul 2011 22:19:16 +0000
treeherdercomm-central@1dbfc673bc3f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8
bugs669040
Bug 669040 part 1: Move mozilla/db/ to comm-central/db/ r=Standard8
db/Makefile.in
db/mork/Makefile.in
db/mork/build/Makefile.in
db/mork/build/nsIMdbFactoryFactory.h
db/mork/build/nsMorkCID.h
db/mork/build/nsMorkFactory.cpp
db/mork/public/Makefile.in
db/mork/public/mdb.h
db/mork/src/Makefile.in
db/mork/src/mork.h
db/mork/src/morkArray.cpp
db/mork/src/morkArray.h
db/mork/src/morkAtom.cpp
db/mork/src/morkAtom.h
db/mork/src/morkAtomMap.cpp
db/mork/src/morkAtomMap.h
db/mork/src/morkAtomSpace.cpp
db/mork/src/morkAtomSpace.h
db/mork/src/morkBead.cpp
db/mork/src/morkBead.h
db/mork/src/morkBlob.cpp
db/mork/src/morkBlob.h
db/mork/src/morkBuilder.cpp
db/mork/src/morkBuilder.h
db/mork/src/morkCell.cpp
db/mork/src/morkCell.h
db/mork/src/morkCellObject.cpp
db/mork/src/morkCellObject.h
db/mork/src/morkCh.cpp
db/mork/src/morkCh.h
db/mork/src/morkConfig.cpp
db/mork/src/morkConfig.h
db/mork/src/morkCursor.cpp
db/mork/src/morkCursor.h
db/mork/src/morkDeque.cpp
db/mork/src/morkDeque.h
db/mork/src/morkEnv.cpp
db/mork/src/morkEnv.h
db/mork/src/morkFactory.cpp
db/mork/src/morkFactory.h
db/mork/src/morkFile.cpp
db/mork/src/morkFile.h
db/mork/src/morkHandle.cpp
db/mork/src/morkHandle.h
db/mork/src/morkIntMap.cpp
db/mork/src/morkIntMap.h
db/mork/src/morkMap.cpp
db/mork/src/morkMap.h
db/mork/src/morkNode.cpp
db/mork/src/morkNode.h
db/mork/src/morkNodeMap.cpp
db/mork/src/morkNodeMap.h
db/mork/src/morkObject.cpp
db/mork/src/morkObject.h
db/mork/src/morkParser.cpp
db/mork/src/morkParser.h
db/mork/src/morkPool.cpp
db/mork/src/morkPool.h
db/mork/src/morkPortTableCursor.cpp
db/mork/src/morkPortTableCursor.h
db/mork/src/morkProbeMap.cpp
db/mork/src/morkProbeMap.h
db/mork/src/morkQuickSort.cpp
db/mork/src/morkQuickSort.h
db/mork/src/morkRow.cpp
db/mork/src/morkRow.h
db/mork/src/morkRowCellCursor.cpp
db/mork/src/morkRowCellCursor.h
db/mork/src/morkRowMap.cpp
db/mork/src/morkRowMap.h
db/mork/src/morkRowObject.cpp
db/mork/src/morkRowObject.h
db/mork/src/morkRowSpace.cpp
db/mork/src/morkRowSpace.h
db/mork/src/morkSearchRowCursor.cpp
db/mork/src/morkSearchRowCursor.h
db/mork/src/morkSink.cpp
db/mork/src/morkSink.h
db/mork/src/morkSpace.cpp
db/mork/src/morkSpace.h
db/mork/src/morkStore.cpp
db/mork/src/morkStore.h
db/mork/src/morkStream.cpp
db/mork/src/morkStream.h
db/mork/src/morkTable.cpp
db/mork/src/morkTable.h
db/mork/src/morkTableRowCursor.cpp
db/mork/src/morkTableRowCursor.h
db/mork/src/morkThumb.cpp
db/mork/src/morkThumb.h
db/mork/src/morkUniqRowCursor.h
db/mork/src/morkWriter.cpp
db/mork/src/morkWriter.h
db/mork/src/morkYarn.cpp
db/mork/src/morkYarn.h
db/mork/src/morkZone.cpp
db/mork/src/morkZone.h
db/mork/src/orkinHeap.cpp
db/mork/src/orkinHeap.h
new file mode 100644
--- /dev/null
+++ b/db/Makefile.in
@@ -0,0 +1,51 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH     = ..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+ifndef NSS_DISABLE_DBM
+ifdef MOZ_MORK
+PARALLEL_DIRS = mork
+endif
+endif 
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/db/mork/Makefile.in
@@ -0,0 +1,48 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS		= src build public
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/db/mork/build/Makefile.in
@@ -0,0 +1,63 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+VPATH		= @srcdir@
+srcdir		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= mork
+LIBRARY_NAME	= mork
+EXPORT_LIBRARY = 1
+IS_COMPONENT	= 1
+MODULE_NAME	= nsMorkModule
+LIBXUL_LIBRARY = 1
+
+
+CPPSRCS		= nsMorkFactory.cpp
+
+EXPORTS		= \
+		nsMorkCID.h \
+		nsIMdbFactoryFactory.h \
+		$(NULL)
+
+SHARED_LIBRARY_LIBS = ../src/$(LIB_PREFIX)msgmork_s.$(LIB_SUFFIX)
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/db/mork/build/nsIMdbFactoryFactory.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsIMdbFactoryFactory_h__
+#define nsIMdbFactoryFactory_h__
+
+#include "nsISupports.h"
+#include "nsIFactory.h"
+#include "nsIComponentManager.h"
+
+class nsIMdbFactory;
+
+// 2794D0B7-E740-47a4-91C0-3E4FCB95B806
+#define NS_IMDBFACTORYFACTORY_IID          \
+{ 0x2794d0b7, 0xe740, 0x47a4, { 0x91, 0xc0, 0x3e, 0x4f, 0xcb, 0x95, 0xb8, 0x6 } }
+
+// because Mork doesn't support XPCOM, we have to wrap the mdb factory interface
+// with an interface that gives you an mdb factory.
+class nsIMdbFactoryService : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFACTORYFACTORY_IID)
+  NS_IMETHOD GetMdbFactory(nsIMdbFactory **aFactory) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFactoryService, NS_IMDBFACTORYFACTORY_IID)
+
+#endif
new file mode 100644
--- /dev/null
+++ b/db/mork/build/nsMorkCID.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsMorkCID_h__
+#define nsMorkCID_h__
+
+#include "nsISupports.h"
+#include "nsIFactory.h"
+#include "nsIComponentManager.h"
+
+#define NS_MORK_CONTRACTID \
+  "@mozilla.org/db/mork;1"
+
+// 36d90300-27f5-11d3-8d74-00805f8a6617
+#define NS_MORK_CID                      \
+{ 0x36d90300, 0x27f5, 0x11d3,                  \
+    { 0x8d, 0x74, 0x00, 0x80, 0x5f, 0x8a, 0x66, 0x17 } }
+
+#endif
new file mode 100644
--- /dev/null
+++ b/db/mork/build/nsMorkFactory.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Pierre Phaneuf <pp@ludusdesign.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "mozilla/ModuleUtils.h"
+#include "nsCOMPtr.h"
+#include "nsMorkCID.h"
+#include "nsIMdbFactoryFactory.h"
+#include "mdb.h"
+
+class nsMorkFactoryService : public nsIMdbFactoryService
+{
+public:
+  nsMorkFactoryService() {};
+  // nsISupports methods
+  NS_DECL_ISUPPORTS 
+
+  NS_IMETHOD GetMdbFactory(nsIMdbFactory **aFactory);
+
+protected:
+  nsCOMPtr<nsIMdbFactory> mMdbFactory;
+};
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMorkFactoryService)
+
+NS_DEFINE_NAMED_CID(NS_MORK_CID);
+
+const mozilla::Module::CIDEntry kMorkCIDs[] = {
+  { &kNS_MORK_CID, false, NULL, nsMorkFactoryServiceConstructor },
+  { NULL }
+};
+
+const mozilla::Module::ContractIDEntry kMorkContracts[] = {
+  { NS_MORK_CONTRACTID, &kNS_MORK_CID },
+  { NULL }
+};
+
+static const mozilla::Module kMorkModule = {
+  mozilla::Module::kVersion,
+  kMorkCIDs,
+  kMorkContracts
+};
+
+NSMODULE_DEFN(nsMorkModule) = &kMorkModule;
+
+NS_IMPL_ISUPPORTS1(nsMorkFactoryService, nsIMdbFactoryService)
+
+NS_IMETHODIMP nsMorkFactoryService::GetMdbFactory(nsIMdbFactory **aFactory)
+{
+  if (!mMdbFactory)
+    mMdbFactory = MakeMdbFactory();
+  NS_IF_ADDREF(*aFactory = mMdbFactory);
+  return *aFactory ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
new file mode 100644
--- /dev/null
+++ b/db/mork/public/Makefile.in
@@ -0,0 +1,51 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1999
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= mork
+XPIDL_MODULE	= msgmdb
+
+EXPORTS		= mdb.h
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/db/mork/public/mdb.h
@@ -0,0 +1,2569 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Blake Ross (blake@blakeross.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#define _MDB_ 1
+
+#include "nscore.h"
+#include "nsISupports.h"
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// { %%%%% begin scalar typedefs %%%%%
+typedef unsigned char  mdb_u1;  // make sure this is one byte
+typedef unsigned short mdb_u2;  // make sure this is two bytes
+typedef short          mdb_i2;  // make sure this is two bytes
+typedef PRUint32       mdb_u4;  // make sure this is four bytes
+typedef PRInt32        mdb_i4;  // make sure this is four bytes
+typedef PRWord         mdb_ip;  // make sure sizeof(mdb_ip) == sizeof(void*)
+
+typedef mdb_u1 mdb_bool;  // unsigned byte with zero=false, nonzero=true
+
+/* canonical boolean constants provided only for code clarity: */
+#define mdbBool_kTrue  ((mdb_bool) 1) /* actually any nonzero means true */
+#define mdbBool_kFalse ((mdb_bool) 0) /* only zero means false */
+
+typedef mdb_u4 mdb_id;    // unsigned object identity in a scope
+typedef mdb_id mdb_rid;          // unsigned row identity inside scope
+typedef mdb_id mdb_tid;          // unsigned table identity inside scope
+typedef mdb_u4 mdb_token; // unsigned token for atomized string
+typedef mdb_token mdb_scope;     // token used to id scope for rows
+typedef mdb_token mdb_kind;      // token used to id kind for tables
+typedef mdb_token mdb_column;    // token used to id columns for rows
+typedef mdb_token mdb_cscode;    // token used to id charset names
+typedef mdb_u4 mdb_seed;  // unsigned collection change counter
+typedef mdb_u4 mdb_count; // unsigned collection member count
+typedef mdb_u4 mdb_size;  // unsigned physical media size
+typedef mdb_u4 mdb_fill;  // unsigned logical content size
+typedef mdb_u4 mdb_more;  // more available bytes for larger buffer
+
+#define mdbId_kNone ((mdb_id) -1) /* never a valid Mork object ID */
+
+typedef mdb_u4 mdb_percent; // 0..100, with values >100 same as 100
+
+typedef mdb_u1 mdb_priority; // 0..9, for a total of ten different values
+
+// temporary substitute for NS_RESULT, for mdb.h standalone compilation:
+typedef nsresult mdb_err;   // equivalent to NS_RESULT
+
+// sequence position is signed; negative is useful to mean "before first":
+typedef mdb_i4 mdb_pos; // signed zero-based ordinal collection position
+
+#define mdbPos_kBeforeFirst ((mdb_pos) -1) /* any negative is before zero */
+
+// order is also signed, so we can use three states for comparison order:
+typedef mdb_i4 mdb_order; // neg:lessthan, zero:equalto, pos:greaterthan 
+
+typedef mdb_order (* mdbAny_Order)(const void* inA, const void* inB, 
+  const void* inClosure);
+
+// } %%%%% end scalar typedefs %%%%%
+
+// { %%%%% begin C structs %%%%%
+
+#ifndef mdbScopeStringSet_typedef
+typedef struct mdbScopeStringSet mdbScopeStringSet;
+#define mdbScopeStringSet_typedef 1
+#endif
+
+/*| mdbScopeStringSet: a set of null-terminated C strings that enumerate some
+**| names of row scopes, so that row scopes intended for use by an application
+**| can be declared by an app when trying to open or create a database file.
+**| (We use strings and not tokens because we cannot know the tokens for any
+**| particular db without having first opened the db.)  The goal is to inform
+**| a db runtime that scopes not appearing in this list can be given relatively
+**| short shrift in runtime representation, with the expectation that other
+**| scopes will not actually be used.  However, a db should still be prepared
+**| to handle accessing row scopes not in this list, rather than raising errors.
+**| But it could be quite expensive to access a row scope not on the list.
+**| Note a zero count for the string set means no such string set is being
+**| specified, and that a db should handle all row scopes efficiently. 
+**| (It does NOT mean an app plans to use no content whatsoever.)
+|*/
+#ifndef mdbScopeStringSet_struct
+#define mdbScopeStringSet_struct 1
+struct mdbScopeStringSet { // vector of scopes for use in db opening policy
+  // when mScopeStringSet_Count is zero, this means no scope constraints 
+  mdb_count     mScopeStringSet_Count;    // number of strings in vector below
+  const char**  mScopeStringSet_Strings;  // null-ended ascii scope strings
+};
+#endif /*mdbScopeStringSet_struct*/
+
+#ifndef mdbOpenPolicy_typedef
+typedef struct mdbOpenPolicy mdbOpenPolicy;
+#define mdbOpenPolicy_typedef 1
+#endif
+
+#ifndef mdbOpenPolicy_struct
+#define mdbOpenPolicy_struct 1
+struct mdbOpenPolicy { // policies affecting db usage for ports and stores
+  mdbScopeStringSet  mOpenPolicy_ScopePlan; // predeclare scope usage plan
+  mdb_bool           mOpenPolicy_MaxLazy;   // nonzero: do least work
+  mdb_bool           mOpenPolicy_MinMemory; // nonzero: use least memory
+};
+#endif /*mdbOpenPolicy_struct*/
+
+#ifndef mdbTokenSet_typedef
+typedef struct mdbTokenSet mdbTokenSet;
+#define mdbTokenSet_typedef 1
+#endif
+
+#ifndef mdbTokenSet_struct
+#define mdbTokenSet_struct 1
+struct mdbTokenSet { // array for a set of tokens, and actual slots used
+  mdb_count   mTokenSet_Count;   // number of token slots in the array
+  mdb_fill    mTokenSet_Fill;    // the subset of count slots actually used
+  mdb_more    mTokenSet_More;    // more tokens available for bigger array
+  mdb_token*  mTokenSet_Tokens;  // array of count mdb_token instances
+};
+#endif /*mdbTokenSet_struct*/
+
+#ifndef mdbUsagePolicy_typedef
+typedef struct mdbUsagePolicy mdbUsagePolicy;
+#define mdbUsagePolicy_typedef 1
+#endif
+
+/*| mdbUsagePolicy: another version of mdbOpenPolicy which uses tokens instead
+**| of scope strings, because usage policies can be constructed for use with a
+**| db that is already open, while an open policy must be constructed before a
+**| db has yet been opened.
+|*/
+#ifndef mdbUsagePolicy_struct
+#define mdbUsagePolicy_struct 1
+struct mdbUsagePolicy { // policies affecting db usage for ports and stores
+  mdbTokenSet  mUsagePolicy_ScopePlan; // current scope usage plan
+  mdb_bool     mUsagePolicy_MaxLazy;   // nonzero: do least work
+  mdb_bool     mUsagePolicy_MinMemory; // nonzero: use least memory
+};
+#endif /*mdbUsagePolicy_struct*/
+
+#ifndef mdbOid_typedef
+typedef struct mdbOid mdbOid;
+#define mdbOid_typedef 1
+#endif
+
+#ifndef mdbOid_struct
+#define mdbOid_struct 1
+struct mdbOid { // identity of some row or table inside a database
+  mdb_scope   mOid_Scope;  // scope token for an id's namespace
+  mdb_id      mOid_Id;     // identity of object inside scope namespace
+};
+#endif /*mdbOid_struct*/
+
+#ifndef mdbRange_typedef
+typedef struct mdbRange mdbRange;
+#define mdbRange_typedef 1
+#endif
+
+#ifndef mdbRange_struct
+#define mdbRange_struct 1
+struct mdbRange { // range of row positions in a table
+  mdb_pos   mRange_FirstPos;  // position of first row
+  mdb_pos   mRange_LastPos;   // position of last row
+};
+#endif /*mdbRange_struct*/
+
+#ifndef mdbColumnSet_typedef
+typedef struct mdbColumnSet mdbColumnSet;
+#define mdbColumnSet_typedef 1
+#endif
+
+#ifndef mdbColumnSet_struct
+#define mdbColumnSet_struct 1
+struct mdbColumnSet { // array of column tokens (just the same as mdbTokenSet)
+  mdb_count    mColumnSet_Count;    // number of columns
+  mdb_column*  mColumnSet_Columns;  // count mdb_column instances
+};
+#endif /*mdbColumnSet_struct*/
+
+#ifndef mdbYarn_typedef
+typedef struct mdbYarn mdbYarn;
+#define mdbYarn_typedef 1
+#endif
+
+#ifdef MDB_BEGIN_C_LINKAGE_define
+#define MDB_BEGIN_C_LINKAGE_define 1
+#define MDB_BEGIN_C_LINKAGE extern "C" {
+#define MDB_END_C_LINKAGE }
+#endif /*MDB_BEGIN_C_LINKAGE_define*/
+
+/*| mdbYarn_mGrow: an abstract API for growing the size of a mdbYarn
+**| instance.  With respect to a specific API that requires a caller
+**| to supply a string (mdbYarn) that a callee fills with content
+**| that might exceed the specified size, mdbYarn_mGrow is a caller-
+**| supplied means of letting a callee attempt to increase the string
+**| size to become large enough to receive all content available.
+**|
+**|| Grow(): a method for requesting that a yarn instance be made
+**| larger in size.  Note that such requests need not be honored, and
+**| need not be honored in full if only partial size growth is desired.
+**| (Note that no nsIMdbEnv instance is passed as argument, although one
+**| might be needed in some circumstances.  So if an nsIMdbEnv is needed,
+**| a reference to one might be held inside a mdbYarn member slot.)
+**|
+**|| self: a yarn instance to be grown.  Presumably this yarn is
+**| the instance which holds the mYarn_Grow method pointer.  Yarn
+**| instancesshould only be passed to grow methods which they were
+**| specifically designed to fit, as indicated by the mYarn_Grow slot.
+**|
+**|| inNewSize: the new desired value for slot mYarn_Size in self.
+**| If mYarn_Size is already this big, then nothing should be done.
+**| If inNewSize is larger than seems feasible or desirable to honor,
+**| then any size restriction policy can be used to grow to some size
+**| greater than mYarn_Size.  (Grow() might even grow to a size
+**| greater than inNewSize in order to make the increase in size seem
+**| worthwhile, rather than growing in many smaller steps over time.)
+|*/
+typedef void (* mdbYarn_mGrow)(mdbYarn* self, mdb_size inNewSize);
+// mdbYarn_mGrow methods must be declared with C linkage in C++
+
+/*| mdbYarn: a variable length "string" of arbitrary binary bytes,
+**| whose length is mYarn_Fill, inside a buffer mYarn_Buf that has
+**| at most mYarn_Size byte of physical space.
+**|
+**|| mYarn_Buf: a pointer to space containing content.  This slot
+**| might never be nil when mYarn_Size is nonzero, but checks for nil
+**| are recommended anyway.
+**| (Implementations of mdbYarn_mGrow methods should take care to
+**| ensure the existence of a replacement before dropping old Bufs.)
+**| Content in Buf can be anything in any format, but the mYarn_Form
+**| implies the actual format by some caller-to-callee convention.
+**| mYarn_Form==0 implies US-ASCII iso-8859-1 Latin1 string content.
+**|
+**|| mYarn_Size: the physical size of Buf in bytes.  Note that if one
+**| intends to terminate a string with a null byte, that it must not
+**| be written at or after mYarn_Buf[mYarn_Size] because this is after
+**| the last byte in the physical buffer space.  Size can be zero,
+**| which means the string has no content whatsoever; note that when
+**| Size is zero, this is a suitable reason for Buf==nil as well.
+**|
+**|| mYarn_Fill: the logical content in Buf in bytes, where Fill must
+**| never exceed mYarn_Size.  Note that yarn strings might not have a
+**| terminating null byte (since they might not even be C strings), but
+**| when they do, such terminating nulls are considered part of content
+**| and therefore Fill will count such null bytes.  So an "empty" C
+**| string will have Fill==1, because content includes one null byte.
+**| Fill does not mean "length" when applied to C strings for this
+**| reason.  However, clients using yarns to hold C strings can infer
+**| that length is equal to Fill-1 (but should take care to handle the
+**| case where Fill==0).  To be paranoid, one can always copy to a
+**| destination with size exceeding Fill, and place a redundant null
+**| byte in the Fill position when this simplifies matters.
+**|
+**|| mYarn_Form: a designation of content format within mYarn_Buf.
+**| The semantics of this slot are the least well defined, since the
+**| actual meaning is context dependent, to the extent that callers
+**| and callees must agree on format encoding conventions when such
+**| are not standardized in many computing contexts.  However, in the
+**| context of a specific mdb database, mYarn_Form is a token for an
+**| atomized string in that database that typically names a preferred
+**| mime type charset designation.  If and when mdbYarn is used for
+**| other purposes away from the mdb interface, folks can use another
+**| convention system for encoding content formats.  However, in all
+**| contexts is it useful to maintain the convention that Form==0
+**| implies Buf contains US-ASCII iso-8859-1 Latin1 string content.
+**|
+**|| mYarn_Grow: either a mdbYarn_mGrow method, or else nil.  When
+**| a mdbYarn_mGrow method is provided, this method can be used to
+**| request a yarn buf size increase.  A caller who constructs the 
+**| original mdbYarn instance decides whether a grow method is necessary
+**| or desirable, and uses only grow methods suitable for the buffering
+**| nature of a specific mdbYarn instance.  (For example, Buf might be a
+**| staticly allocated string space which switches to something heap-based
+**| when grown, and subsequent calls to grow the yarn must distinguish the
+**| original static string from heap allocated space, etc.) Note that the
+**| method stored in mYarn_Grow can change, and this might be a common way
+**| to track memory managent changes in policy for mYarn_Buf.
+|*/
+#ifndef mdbYarn_struct
+#define mdbYarn_struct 1
+struct mdbYarn { // buffer with caller space allocation semantics
+  void*         mYarn_Buf;   // space for holding any binary content
+  mdb_fill      mYarn_Fill;  // logical content in Buf in bytes
+  mdb_size      mYarn_Size;  // physical size of Buf in bytes
+  mdb_more      mYarn_More;  // more available bytes if Buf is bigger
+  mdb_cscode    mYarn_Form;  // charset format encoding
+  mdbYarn_mGrow mYarn_Grow;  // optional method to grow mYarn_Buf
+  
+  // Subclasses might add further slots after mYarn_Grow in order to
+  // maintain bookkeeping needs, such as state info about mYarn_Buf.
+};
+#endif /*mdbYarn_struct*/
+
+// } %%%%% end C structs %%%%%
+
+// { %%%%% begin class forward defines %%%%%
+class nsIMdbEnv;
+class nsIMdbObject;
+class nsIMdbErrorHook;
+class nsIMdbCompare;
+class nsIMdbThumb;
+class nsIMdbFactory;
+class nsIMdbFile;
+class nsIMdbPort;
+class nsIMdbStore;
+class nsIMdbCursor;
+class nsIMdbPortTableCursor;
+class nsIMdbCollection;
+class nsIMdbTable;
+class nsIMdbTableRowCursor;
+class nsIMdbRow;
+class nsIMdbRowCellCursor;
+class nsIMdbBlob;
+class nsIMdbCell;
+class nsIMdbSorting;
+// } %%%%% end class forward defines %%%%%
+
+
+// { %%%%% begin C++ abstract class interfaces %%%%%
+
+/*| nsIMdbObject: base class for all message db class interfaces
+**|
+**|| factory: all nsIMdbObjects from the same code suite have the same factory
+**|
+**|| refcounting: both strong and weak references, to ensure strong refs are
+**| acyclic, while weak refs can cause cycles.  CloseMdbObject() is
+**| called when (strong) use counts hit zero, but clients can call this close
+**| method early for some reason, if absolutely necessary even though it will
+**| thwart the other uses of the same object.  Note that implementations must
+**| cope with close methods being called arbitrary numbers of times.  The COM
+**| calls to AddRef() and release ref map directly to strong use ref calls,
+**| but the total ref count for COM objects is the sum of weak & strong refs.
+|*/
+
+#define NS_IMDBOBJECT_IID_STR "5533ea4b-14c3-4bef-ac60-22f9e9a49084"
+
+#define NS_IMDBOBJECT_IID \
+{0x5533ea4b, 0x14c3, 0x4bef, \
+{ 0xac, 0x60, 0x22, 0xf9, 0xe9, 0xa4, 0x90, 0x84}}
+
+class nsIMdbObject : public nsISupports { // msg db base class
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBOBJECT_IID)
+// { ===== begin nsIMdbObject methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) = 0;
+  // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+  // } ----- end attribute methods -----
+
+  // { ----- begin factory methods -----
+  NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory) = 0; 
+  // } ----- end factory methods -----
+
+  // { ----- begin ref counting for well-behaved cyclic graphs -----
+  NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
+    mdb_count* outCount) = 0;  
+  NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
+    mdb_count* outCount) = 0;
+
+  NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) = 0;
+  NS_IMETHOD AddStrongRef(nsIMdbEnv* ev) = 0;
+
+  NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) = 0;
+  NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) = 0;
+  
+  NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) = 0; // called at strong refs zero
+  NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) = 0;
+  // } ----- end ref counting -----
+  
+// } ===== end nsIMdbObject methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbObject, NS_IMDBOBJECT_IID)
+
+/*| nsIMdbErrorHook: a base class for clients of this API to subclass, in order
+**| to provide a callback installable in nsIMdbEnv for error notifications. If
+**| apps that subclass nsIMdbErrorHook wish to maintain a reference to the env
+**| that contains the hook, then this should be a weak ref to avoid cycles.
+**|
+**|| OnError: when nsIMdbEnv has an error condition that causes the total count
+**| of errors to increase, then nsIMdbEnv should call OnError() to report the
+**| error in some fashion when an instance of nsIMdbErrorHook is installed.  The
+**| variety of string flavors is currently due to the uncertainty here in the
+**| nsIMdbBlob and nsIMdbCell interfaces.  (Note that overloading by using the
+**| same method name is not necessary here, and potentially less clear.)
+|*/
+class nsIMdbErrorHook : public nsISupports{ // env callback handler to report errors
+public:
+
+// { ===== begin error methods =====
+  NS_IMETHOD OnErrorString(nsIMdbEnv* ev, const char* inAscii) = 0;
+  NS_IMETHOD OnErrorYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
+// } ===== end error methods =====
+
+// { ===== begin warning methods =====
+  NS_IMETHOD OnWarningString(nsIMdbEnv* ev, const char* inAscii) = 0;
+  NS_IMETHOD OnWarningYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
+// } ===== end warning methods =====
+
+// { ===== begin abort hint methods =====
+  NS_IMETHOD OnAbortHintString(nsIMdbEnv* ev, const char* inAscii) = 0;
+  NS_IMETHOD OnAbortHintYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
+// } ===== end abort hint methods =====
+};
+
+/*| nsIMdbCompare: a caller-supplied yarn comparison interface.  When two yarns
+**| are compared to each other with Order(), this method should return a signed
+**| long integer denoting relation R between the 1st and 2nd yarn instances
+**| such that (First R Second), where negative is less than, zero is equal to,
+**| and positive is greater than.  Note that both yarns are readonly, and the
+**| Order() method should make no attempt to modify the yarn content.
+|*/
+class nsIMdbCompare { // caller-supplied yarn comparison
+public:
+
+// { ===== begin nsIMdbCompare methods =====
+  NS_IMETHOD Order(nsIMdbEnv* ev,      // compare first to second yarn
+    const mdbYarn* inFirst,   // first yarn in comparison
+    const mdbYarn* inSecond,  // second yarn in comparison
+    mdb_order* outOrder) = 0; // negative="<", zero="=", positive=">"
+    
+  NS_IMETHOD AddStrongRef(nsIMdbEnv* ev) = 0; // does nothing
+  NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) = 0; // does nothing
+// } ===== end nsIMdbCompare methods =====
+  
+};
+
+/*| nsIMdbHeap: abstract memory allocation interface. 
+**|
+**|| Alloc: return a block at least inSize bytes in size with alignment
+**| suitable for any native type (such as long integers).  When no such
+**| block can be allocated, failure is indicated by a null address in
+**| addition to reporting an error in the environment.
+**|
+**|| Free: deallocate a block allocated or resized earlier by the same
+**| heap instance.  If the inBlock parameter is nil, the heap should do
+**| nothing (and crashing is strongly discouraged).
+|*/
+class nsIMdbHeap { // caller-supplied memory management interface
+public:
+// { ===== begin nsIMdbHeap methods =====
+  NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory
+    mdb_size inSize,        // requested byte size of new memory block 
+    void** outBlock) = 0;   // memory block of inSize bytes, or nil
+    
+  NS_IMETHOD Free(nsIMdbEnv* ev, // free block from Alloc or Resize()
+    void* ioBlock) = 0;     // block to be destroyed/deallocated
+    
+  NS_IMETHOD HeapAddStrongRef(nsIMdbEnv* ev) = 0;
+  NS_IMETHOD HeapCutStrongRef(nsIMdbEnv* ev) = 0;
+    
+// } ===== end nsIMdbHeap methods =====
+};
+
+/*| nsIMdbCPlusHeap: Alloc() with global ::new(), Free() with global ::delete(). 
+**| Resize() is done by ::new() followed by ::delete().
+|*/
+class nsIMdbCPlusHeap { // caller-supplied memory management interface
+public:
+// { ===== begin nsIMdbHeap methods =====
+  NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory
+    mdb_size inSize,   // requested size of new memory block 
+    void** outBlock);  // memory block of inSize bytes, or nil
+    
+  NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc()
+    void* inBlock);
+    
+  NS_IMETHOD HeapAddStrongRef(nsIMdbEnv* ev);
+  NS_IMETHOD HeapCutStrongRef(nsIMdbEnv* ev);
+// } ===== end nsIMdbHeap methods =====
+};
+
+/*| nsIMdbThumb: 
+|*/
+
+
+#define NS_IMDBTHUMB_IID_STR "6d3ad7c1-a809-4e74-8577-49fa9a4562fa"
+
+#define NS_IMDBTHUMB_IID \
+{0x6d3ad7c1, 0xa809, 0x4e74, \
+{ 0x85, 0x77, 0x49, 0xfa, 0x9a, 0x45, 0x62, 0xfa}}
+
+
+class nsIMdbThumb : public nsISupports { // closure for repeating incremental method
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTHUMB_IID)
+
+// { ===== begin nsIMdbThumb methods =====
+  NS_IMETHOD GetProgress(nsIMdbEnv* ev,
+    mdb_count* outTotal,    // total somethings to do in operation
+    mdb_count* outCurrent,  // subportion of total completed so far
+    mdb_bool* outDone,      // is operation finished?
+    mdb_bool* outBroken     // is operation irreparably dead and broken?
+  ) = 0;
+  
+  NS_IMETHOD DoMore(nsIMdbEnv* ev,
+    mdb_count* outTotal,    // total somethings to do in operation
+    mdb_count* outCurrent,  // subportion of total completed so far
+    mdb_bool* outDone,      // is operation finished?
+    mdb_bool* outBroken     // is operation irreparably dead and broken?
+  ) = 0;
+  
+  NS_IMETHOD CancelAndBreakThumb( // cancel pending operation
+    nsIMdbEnv* ev) = 0;
+// } ===== end nsIMdbThumb methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbThumb, NS_IMDBTHUMB_IID)
+
+/*| nsIMdbEnv: a context parameter used when calling most abstract db methods.
+**| The main purpose of such an object is to permit a database implementation
+**| to avoid the use of globals to share information between various parts of
+**| the implementation behind the abstract db interface.  An environment acts
+**| like a session object for a given calling thread, and callers should use
+**| at least one different nsIMdbEnv instance for each thread calling the API.
+**| While the database implementation might not be threaded, it is highly
+**| desirable that the db be thread-safe if calling threads use distinct
+**| instances of nsIMdbEnv.  Callers can stop at one nsIMdbEnv per thread, or they
+**| might decide to make on nsIMdbEnv instance for every nsIMdbPort opened, so that
+**| error information is segregated by database instance.  Callers create
+**| instances of nsIMdbEnv by calling the MakeEnv() method in nsIMdbFactory. 
+**|
+**|| tracing: an environment might support some kind of tracing, and this
+**| boolean attribute permits such activity to be enabled or disabled.
+**|
+**|| errors: when a call to the abstract db interface returns, a caller might
+**| check the number of outstanding errors to see whether the operation did
+**| actually succeed. Each nsIMdbEnv should have all its errors cleared by a
+**| call to ClearErrors() before making each call to the abstract db API,
+**| because outstanding errors might disable further database actions.  (This
+**| is not done inside the db interface, because the db cannot in general know
+**| when a call originates from inside or outside -- only the app knows this.)
+**|
+**|| error hook: callers can install an instance of nsIMdbErrorHook to receive
+**| error notifications whenever the error count increases.  The hook can
+**| be uninstalled by passing a null pointer.
+**|
+|*/
+
+#define NS_IMDBENV_IID_STR "a765e46b-efb6-41e6-b75b-c5d6bd710594"
+
+#define NS_IMDBENV_IID \
+{0xa765e46b, 0xefb6, 0x41e6, \
+{ 0xb7, 0x5b, 0xc5, 0xd6, 0xbd, 0x71, 0x05, 0x94}}
+
+class nsIMdbEnv : public nsISupports { // db specific context parameter
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBENV_IID)
+// { ===== begin nsIMdbEnv methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetErrorCount(mdb_count* outCount,
+    mdb_bool* outShouldAbort) = 0;
+  NS_IMETHOD GetWarningCount(mdb_count* outCount,
+    mdb_bool* outShouldAbort) = 0;
+  
+  NS_IMETHOD GetEnvBeVerbose(mdb_bool* outBeVerbose) = 0;
+  NS_IMETHOD SetEnvBeVerbose(mdb_bool inBeVerbose) = 0;
+  
+  NS_IMETHOD GetDoTrace(mdb_bool* outDoTrace) = 0;
+  NS_IMETHOD SetDoTrace(mdb_bool inDoTrace) = 0;
+  
+  NS_IMETHOD GetAutoClear(mdb_bool* outAutoClear) = 0;
+  NS_IMETHOD SetAutoClear(mdb_bool inAutoClear) = 0;
+  
+  NS_IMETHOD GetErrorHook(nsIMdbErrorHook** acqErrorHook) = 0;
+  NS_IMETHOD SetErrorHook(
+    nsIMdbErrorHook* ioErrorHook) = 0; // becomes referenced
+  
+  NS_IMETHOD GetHeap(nsIMdbHeap** acqHeap) = 0;
+  NS_IMETHOD SetHeap(
+    nsIMdbHeap* ioHeap) = 0; // becomes referenced
+  // } ----- end attribute methods -----
+  
+  NS_IMETHOD ClearErrors() = 0; // clear errors beore re-entering db API
+  NS_IMETHOD ClearWarnings() = 0; // clear warnings
+  NS_IMETHOD ClearErrorsAndWarnings() = 0; // clear both errors & warnings
+// } ===== end nsIMdbEnv methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbEnv, NS_IMDBENV_IID)
+
+/*| nsIMdbFactory: the main entry points to the abstract db interface.  A DLL
+**| that supports this mdb interface need only have a single exported method
+**| that will return an instance of nsIMdbFactory, so that further methods in
+**| the suite can be accessed from objects returned by nsIMdbFactory methods.
+**|
+**|| mdbYarn: note all nsIMdbFactory subclasses must guarantee null
+**| termination of all strings written into mdbYarn instances, as long as
+**| mYarn_Size and mYarn_Buf are nonzero.  Even truncated string values must
+**| be null terminated.  This is more strict behavior than mdbYarn requires,
+**| but it is part of the nsIMdbFactory interface.
+**|
+**|| envs: an environment instance is required as per-thread context for
+**| most of the db method calls, so nsIMdbFactory creates such instances.
+**|
+**|| rows: callers must be able to create row instances that are independent
+**| of storage space that is part of the db content graph.  Many interfaces
+**| for data exchange have strictly copy semantics, so that a row instance
+**| has no specific identity inside the db content model, and the text in
+**| cells are an independenty copy of unexposed content inside the db model.
+**| Callers are expected to maintain one or more row instances as a buffer
+**| for staging cell content copied into or out of a table inside the db.
+**| Callers are urged to use an instance of nsIMdbRow created by the nsIMdbFactory
+**| code suite, because reading and writing might be much more efficient than
+**| when using a hand-rolled nsIMdbRow subclass with no relation to the suite.
+**|
+**|| ports: a port is a readonly interface to a specific database file. Most
+**| of the methods to access a db file are suitable for a readonly interface,
+**| so a port is the basic minimum for accessing content.  This makes it
+**| possible to read other external formats for import purposes, without
+**| needing the code or competence necessary to write every such format.  So
+**| we can write generic import code just once, as long as every format can
+**| show a face based on nsIMdbPort. (However, same suite import can be faster.)
+**| Given a file name and the first 512 bytes of a file, a factory can say if
+**| a port can be opened by this factory.  Presumably an app maintains chains
+**| of factories for different suites, and asks each in turn about opening a
+**| a prospective file for reading (as a port) or writing (as a store).  I'm
+**| not ready to tackle issues of format fidelity and factory chain ordering.
+**|
+**|| stores: a store is a mutable interface to a specific database file, and
+**| includes the port interface plus any methods particular to writing, which
+**| are few in number.  Presumably the set of files that can be opened as
+**| stores is a subset of the set of files that can be opened as ports.  A
+**| new store can be created with CreateNewFileStore() by supplying a new
+**| file name which does not yet exist (callers are always responsible for
+**| destroying any existing files before calling this method). 
+|*/
+
+#define NS_IMDBFACTORY_IID_STR "2b80395c-b91e-4990-b1a7-023e99ab14e9"
+
+#define NS_IMDBFACTORY_IID \
+{0xf04aa4ab, 0x1fe, 0x4115, \
+{ 0xa4, 0xa5, 0x68, 0x19, 0xdf, 0xf1, 0x10, 0x3d}}
+
+
+class nsIMdbFactory : public nsISupports { // suite entry points
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFACTORY_IID)
+// { ===== begin nsIMdbFactory methods =====
+
+  // { ----- begin file methods -----
+  NS_IMETHOD OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+    const char* inFilePath,
+    mdb_bool inFrozen, nsIMdbFile** acqFile) = 0;
+  // Choose some subclass of nsIMdbFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be open and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+
+  NS_IMETHOD CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+    const char* inFilePath,
+    nsIMdbFile** acqFile) = 0;
+  // Choose some subclass of nsIMdbFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be created and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+  // } ----- end file methods -----
+
+  // { ----- begin env methods -----
+  NS_IMETHOD MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv) = 0; // acquire new env
+  // ioHeap can be nil, causing a MakeHeap() style heap instance to be used
+  // } ----- end env methods -----
+
+  // { ----- begin heap methods -----
+  NS_IMETHOD MakeHeap(nsIMdbEnv* ev, nsIMdbHeap** acqHeap) = 0; // acquire new heap
+  // } ----- end heap methods -----
+
+  // { ----- begin compare methods -----
+  NS_IMETHOD MakeCompare(nsIMdbEnv* ev, nsIMdbCompare** acqCompare) = 0; // ASCII
+  // } ----- end compare methods -----
+
+  // { ----- begin row methods -----
+  NS_IMETHOD MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbRow** acqRow) = 0; // new row
+  // ioHeap can be nil, causing the heap associated with ev to be used
+  // } ----- end row methods -----
+  
+  // { ----- begin port methods -----
+  NS_IMETHOD CanOpenFilePort(
+    nsIMdbEnv* ev, // context
+    // const char* inFilePath, // the file to investigate
+    // const mdbYarn* inFirst512Bytes,
+    nsIMdbFile* ioFile, // db abstract file interface
+    mdb_bool* outCanOpen, // whether OpenFilePort() might succeed
+    mdbYarn* outFormatVersion) = 0; // informal file format description
+    
+  NS_IMETHOD OpenFilePort(
+    nsIMdbEnv* ev, // context
+    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+    // const char* inFilePath, // the file to open for readonly import
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental port open
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance.
+
+  NS_IMETHOD ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort()
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status
+    nsIMdbPort** acqPort) = 0; // acquire new port object
+  // } ----- end port methods -----
+  
+  // { ----- begin store methods -----
+  NS_IMETHOD CanOpenFileStore(
+    nsIMdbEnv* ev, // context
+    // const char* inFilePath, // the file to investigate
+    // const mdbYarn* inFirst512Bytes,
+    nsIMdbFile* ioFile, // db abstract file interface
+    mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed
+    mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed
+    mdbYarn* outFormatVersion) = 0; // informal file format description
+    
+  NS_IMETHOD OpenFileStore( // open an existing database
+    nsIMdbEnv* ev, // context
+    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+    // const char* inFilePath, // the file to open for general db usage
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental store open
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance.
+    
+  NS_IMETHOD
+  ThumbToOpenStore( // redeem completed thumb from OpenFileStore()
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status
+    nsIMdbStore** acqStore) = 0; // acquire new db store object
+  
+  NS_IMETHOD CreateNewFileStore( // create a new db with minimal content
+    nsIMdbEnv* ev, // context
+    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+    // const char* inFilePath, // name of file which should not yet exist
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+    nsIMdbStore** acqStore) = 0; // acquire new db store object
+  // } ----- end store methods -----
+
+// } ===== end nsIMdbFactory methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFactory, NS_IMDBFACTORY_IID)
+
+extern "C" nsIMdbFactory* MakeMdbFactory(); 
+
+/*| nsIMdbFile: abstract file interface resembling the original morkFile
+**| abstract interface (which was in turn modeled on the file interface
+**| from public domain IronDoc).  The design of this file interface is
+**| complicated by the fact that some DB's will not find this interface
+**| adequate for all runtime requirements (even though this file API is
+**| enough to implement text-based DB's like Mork).  For this reason,
+**| more methods have been added to let a DB library force the file to
+**| become closed so the DB can reopen the file in some other manner.
+**| Folks are encouraged to suggest ways to tune this interface to suit
+**| DB's that cannot manage to pull their maneuvers even given this API.
+**|
+**|| Tell: get the current i/o position in file
+**|
+**|| Seek: change the current i/o position in file
+**|
+**|| Eof: return file's total length in bytes
+**|
+**|| Read: input inSize bytes into outBuf, returning actual transfer size
+**|
+**|| Get: read starting at specific file offset (e.g. Seek(); Read();)
+**|
+**|| Write: output inSize bytes from inBuf, returning actual transfer size
+**|
+**|| Put: write starting at specific file offset (e.g. Seek(); Write();)
+**|
+**|| Flush: if written bytes are buffered, push them to final destination
+**|
+**|| Path: get file path in some string representation.  This is intended
+**| either to support the display of file name in a user presentation, or
+**| to support the closing and reopening of the file when the DB needs more
+**| exotic file access than is presented by the nsIMdbFile interface.
+**|
+**|| Steal: tell this file to close any associated i/o stream in the file
+**| system, because the file ioThief intends to reopen the file in order
+**| to provide the MDB implementation with more exotic file access than is
+**| offered by the nsIMdbFile alone.  Presumably the thief knows enough
+**| from Path() in order to know which file to reopen.  If Steal() is
+**| successful, this file should probably delegate all future calls to
+**| the nsIMdbFile interface down to the thief files, so that even after
+**| the file has been stolen, it can still be read, written, or forcibly
+**| closed (by a call to CloseMdbObject()).
+**|
+**|| Thief: acquire and return thief passed to an earlier call to Steal().
+|*/
+
+#define NS_IMDBFILE_IID_STR "f04aa4ab-1fe7-4115-a4a5-6819dff1103d"
+
+#define NS_IMDBFILE_IID \
+{0xf04aa4ab, 0x1fe, 0x4115, \
+{ 0xa4, 0xa5, 0x68, 0x19, 0xdf, 0xf1, 0x10, 0x3d}}
+
+class nsIMdbFile : public nsISupports { // minimal file interface
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFILE_IID)
+// { ===== begin nsIMdbFile methods =====
+
+  // { ----- begin pos methods -----
+  NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const = 0;
+  NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos) = 0;
+  NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos) = 0;
+  // } ----- end pos methods -----
+
+  // { ----- begin read methods -----
+  NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+    mdb_size* outActualSize) = 0;
+  NS_IMETHOD Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+    mdb_pos inPos, mdb_size* outActualSize) = 0;
+  // } ----- end read methods -----
+    
+  // { ----- begin write methods -----
+  NS_IMETHOD  Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+    mdb_size* outActualSize) = 0;
+  NS_IMETHOD  Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+    mdb_pos inPos, mdb_size* outActualSize) = 0;
+  NS_IMETHOD  Flush(nsIMdbEnv* ev) = 0;
+  // } ----- end attribute methods -----
+    
+  // { ----- begin path methods -----
+  NS_IMETHOD  Path(nsIMdbEnv* ev, mdbYarn* outFilePath) = 0;
+  // } ----- end path methods -----
+    
+  // { ----- begin replacement methods -----
+  NS_IMETHOD  Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) = 0;
+  NS_IMETHOD  Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief) = 0;
+  // } ----- end replacement methods -----
+
+  // { ----- begin versioning methods -----
+  NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) = 0;
+  // If this file is a file version branch created by calling AcquireBud(),
+  // BecomeTrunk() causes this file's content to replace the original
+  // file's content, typically by assuming the original file's identity.
+  // This default implementation of BecomeTrunk() does nothing, and this
+  // is appropriate behavior for files which are not branches, and is
+  // also the right behavior for files returned from AcquireBud() which are
+  // in fact the original file that has been truncated down to zero length.
+
+  NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+    nsIMdbFile** acqBud) = 0; // acquired file for new version of content
+  // AcquireBud() starts a new "branch" version of the file, empty of content,
+  // so that a new version of the file can be written.  This new file
+  // can later be told to BecomeTrunk() the original file, so the branch
+  // created by budding the file will replace the original file.  Some
+  // file subclasses might initially take the unsafe but expedient
+  // approach of simply truncating this file down to zero length, and
+  // then returning the same morkFile pointer as this, with an extra
+  // reference count increment.  Note that the caller of AcquireBud() is
+  // expected to eventually call CutStrongRef() on the returned file
+  // in order to release the strong reference.  High quality versions
+  // of morkFile subclasses will create entirely new files which later
+  // are renamed to become the old file, so that better transactional
+  // behavior is exhibited by the file, so crashes protect old files.
+  // Note that AcquireBud() is an illegal operation on readonly files.
+  // } ----- end versioning methods -----
+
+// } ===== end nsIMdbFile methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFile, NS_IMDBFILE_IID)
+
+/*| nsIMdbPort: a readonly interface to a specific database file. The mutable
+**| nsIMdbStore interface is a subclass that includes writing behavior, but
+**| most of the needed db methods appear in the readonly nsIMdbPort interface.
+**|
+**|| mdbYarn: note all nsIMdbPort and nsIMdbStore subclasses must guarantee null
+**| termination of all strings written into mdbYarn instances, as long as
+**| mYarn_Size and mYarn_Buf are nonzero.  Even truncated string values must
+**| be null terminated.  This is more strict behavior than mdbYarn requires,
+**| but it is part of the nsIMdbPort and nsIMdbStore interface.
+**|
+**|| attributes: methods are provided to distinguish a readonly port from a
+**| mutable store, and whether a mutable store actually has any dirty content.
+**|
+**|| filepath: the file path used to open the port from the nsIMdbFactory can be
+**| queried and discovered by GetPortFilePath(), which includes format info.
+**|
+**|| export: a port can write itself in other formats, with perhaps a typical
+**| emphasis on text interchange formats used by other systems.  A port can be
+**| queried to determine its preferred export interchange format, and a port
+**| can be queried to see whether a specific export format is supported.  And
+**| actually exporting a port requires a new destination file name and format.
+**|
+**|| tokens: a port supports queries about atomized strings to map tokens to
+**| strings or strings to token integers.  (All atomized strings must be in
+**| US-ASCII iso-8859-1 Latin1 charset encoding.)  When a port is actually a
+**| mutable store and a string has not yet been atomized, then StringToToken()
+**| will actually do so and modify the store.  The QueryToken() method will not
+**| atomize a string if it has not already been atomized yet, even in stores.
+**|
+**|| tables: other than string tokens, all port content is presented through
+**| tables, which are ordered collections of rows.  Tables are identified by
+**| row scope and table kind, which might or might not be unique in a port,
+**| depending on app convention.  When tables are effectively unique, then
+**| queries for specific scope and kind pairs will find those tables.  To see
+**| all tables that match specific row scope and table kind patterns, even in
+**| the presence of duplicates, every port supports a GetPortTableCursor()
+**| method that returns an iterator over all matching tables.  Table kind is
+**| considered scoped inside row scope, so passing a zero for table kind will
+**| find all table kinds for some nonzero row scope.  Passing a zero for row
+**| scope will iterate over all tables in the port, in some undefined order.
+**| (A new table can be added to a port using nsIMdbStore::NewTable(), even when
+**| the requested scope and kind combination is already used by other tables.)
+**|
+**|| memory: callers can request that a database use less memory footprint in
+**| several flavors, from an inconsequential idle flavor to a rather drastic
+**| panic flavor. Callers might perform an idle purge very frequently if desired
+**| with very little cost, since only normally scheduled memory management will
+**| be conducted, such as freeing resources for objects scheduled to be dropped.
+**| Callers should perform session memory purges infrequently because they might
+**| involve costly scanning of data structures to removed cached content, and
+**| session purges are recommended only when a caller experiences memory crunch.
+**| Callers should only rarely perform a panic purge, in response to dire memory
+**| straits, since this is likely to make db operations much more expensive
+**| than they would be otherwise.  A panic purge asks a database to free as much
+**| memory as possible while staying effective and operational, because a caller
+**| thinks application failure might otherwise occur.  (Apps might better close
+**| an open db, so panic purges only make sense when a db is urgently needed.)
+|*/
+class nsIMdbPort : public nsISupports {
+public:
+
+// { ===== begin nsIMdbPort methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) = 0;
+  NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) = 0;
+  NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) = 0;
+
+  NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev, 
+    mdbUsagePolicy* ioUsagePolicy) = 0;
+
+  NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev, 
+    const mdbUsagePolicy* inUsagePolicy) = 0;
+  // } ----- end attribute methods -----
+
+  // { ----- begin memory policy methods -----  
+  NS_IMETHOD IdleMemoryPurge( // do memory management already scheduled
+    nsIMdbEnv* ev, // context
+    mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed
+
+  NS_IMETHOD SessionMemoryPurge( // request specific footprint decrease
+    nsIMdbEnv* ev, // context
+    mdb_size inDesiredBytesFreed, // approximate number of bytes wanted
+    mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed
+
+  NS_IMETHOD PanicMemoryPurge( // desperately free all possible memory
+    nsIMdbEnv* ev, // context
+    mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed
+  // } ----- end memory policy methods -----
+
+  // { ----- begin filepath methods -----
+  NS_IMETHOD GetPortFilePath(
+    nsIMdbEnv* ev, // context
+    mdbYarn* outFilePath, // name of file holding port content
+    mdbYarn* outFormatVersion) = 0; // file format description
+    
+  NS_IMETHOD GetPortFile(
+    nsIMdbEnv* ev, // context
+    nsIMdbFile** acqFile) = 0; // acquire file used by port or store
+  // } ----- end filepath methods -----
+
+  // { ----- begin export methods -----
+  NS_IMETHOD BestExportFormat( // determine preferred export format
+    nsIMdbEnv* ev, // context
+    mdbYarn* outFormatVersion) = 0; // file format description
+
+  // some tentative suggested import/export formats
+  // "ns:msg:db:port:format:ldif:ns4.0:passthrough" // necessary
+  // "ns:msg:db:port:format:ldif:ns4.5:utf8"        // necessary
+  // "ns:msg:db:port:format:ldif:ns4.5:tabbed"
+  // "ns:msg:db:port:format:ldif:ns4.5:binary"      // necessary
+  // "ns:msg:db:port:format:html:ns3.0:addressbook" // necessary
+  // "ns:msg:db:port:format:html:display:verbose"
+  // "ns:msg:db:port:format:html:display:concise"
+  // "ns:msg:db:port:format:mork:zany:verbose"      // necessary
+  // "ns:msg:db:port:format:mork:zany:atomized"     // necessary
+  // "ns:msg:db:port:format:rdf:xml"
+  // "ns:msg:db:port:format:xml:mork"
+  // "ns:msg:db:port:format:xml:display:verbose"
+  // "ns:msg:db:port:format:xml:display:concise"
+  // "ns:msg:db:port:format:xml:print:verbose"      // recommended
+  // "ns:msg:db:port:format:xml:print:concise"
+
+  NS_IMETHOD
+  CanExportToFormat( // can export content in given specific format?
+    nsIMdbEnv* ev, // context
+    const char* inFormatVersion, // file format description
+    mdb_bool* outCanExport) = 0; // whether ExportSource() might succeed
+
+  NS_IMETHOD ExportToFormat( // export content in given specific format
+    nsIMdbEnv* ev, // context
+    // const char* inFilePath, // the file to receive exported content
+    nsIMdbFile* ioFile, // destination abstract file interface
+    const char* inFormatVersion, // file format description
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental export
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the export will be finished.
+
+  // } ----- end export methods -----
+
+  // { ----- begin token methods -----
+  NS_IMETHOD TokenToString( // return a string name for an integer token
+    nsIMdbEnv* ev, // context
+    mdb_token inToken, // token for inTokenName inside this port
+    mdbYarn* outTokenName) = 0; // the type of table to access
+  
+  NS_IMETHOD StringToToken( // return an integer token for scope name
+    nsIMdbEnv* ev, // context
+    const char* inTokenName, // Latin1 string to tokenize if possible
+    mdb_token* outToken) = 0; // token for inTokenName inside this port
+    
+  // String token zero is never used and never supported. If the port
+  // is a mutable store, then StringToToken() to create a new
+  // association of inTokenName with a new integer token if possible.
+  // But a readonly port will return zero for an unknown scope name.
+
+  NS_IMETHOD QueryToken( // like StringToToken(), but without adding
+    nsIMdbEnv* ev, // context
+    const char* inTokenName, // Latin1 string to tokenize if possible
+    mdb_token* outToken) = 0; // token for inTokenName inside this port
+  
+  // QueryToken() will return a string token if one already exists,
+  // but unlike StringToToken(), will not assign a new token if not
+  // already in use.
+
+  // } ----- end token methods -----
+
+  // { ----- begin row methods -----  
+  NS_IMETHOD HasRow( // contains a row with the specified oid?
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical row oid
+    mdb_bool* outHasRow) = 0; // whether GetRow() might succeed
+
+  NS_IMETHOD GetRowRefCount( // get number of tables that contain a row 
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical row oid
+    mdb_count* outRefCount) = 0; // number of tables containing inRowKey 
+    
+  NS_IMETHOD GetRow( // access one row with specific oid
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical row oid
+    nsIMdbRow** acqRow) = 0; // acquire specific row (or null)
+    
+  // NS_IMETHOD
+  // GetPortRowCursor( // get cursor for all rows in specific scope
+  //   nsIMdbEnv* ev, // context
+  //   mdb_scope inRowScope, // row scope for row ids
+  //   nsIMdbPortRowCursor** acqCursor) = 0; // all such rows in the port
+
+  NS_IMETHOD FindRow(nsIMdbEnv* ev, // search for row with matching cell
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_column inColumn,   // the column to search (and maintain an index)
+    const mdbYarn* inTargetCellValue, // cell value for which to search
+    mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match)
+    nsIMdbRow** acqRow) = 0; // acquire matching row (or nil for no match)
+                             // can be null if you only want the oid
+  // FindRow() searches for one row that has a cell in column inColumn with
+  // a contained value with the same form (i.e. charset) and is byte-wise
+  // identical to the blob described by yarn inTargetCellValue.  Both content
+  // and form of the yarn must be an exact match to find a matching row.
+  //
+  // (In other words, both a yarn's blob bytes and form are significant.  The
+  // form is not expected to vary in columns used for identity anyway.  This
+  // is intended to make the cost of FindRow() cheaper for MDB implementors,
+  // since any cell value atomization performed internally must necessarily
+  // make yarn form significant in order to avoid data loss in atomization.)
+  //
+  // FindRow() can lazily create an index on attribute inColumn for all rows
+  // with that attribute in row space scope inRowScope, so that subsequent
+  // calls to FindRow() will perform faster.  Such an index might or might
+  // not be persistent (but this seems desirable if it is cheap to do so).
+  // Note that lazy index creation in readonly DBs is not very feasible.
+  //
+  // This FindRow() interface assumes that attribute inColumn is effectively
+  // an alternative means of unique identification for a row in a rowspace,
+  // so correct behavior is only guaranteed when no duplicates for this col
+  // appear in the given set of rows.  (If more than one row has the same cell
+  // value in this column, no more than one will be found; and cutting one of
+  // two duplicate rows can cause the index to assume no other such row lives
+  // in the row space, so future calls return nil for negative search results
+  // even though some duplicate row might still live within the rowspace.)
+  //
+  // In other words, the FindRow() implementation is allowed to assume simple
+  // hash tables mapping unqiue column keys to associated row values will be
+  // sufficient, where any duplication is not recorded because only one copy
+  // of a given key need be remembered.  Implementors are not required to sort
+  // all rows by the specified column.
+  // } ----- end row methods -----
+
+  // { ----- begin table methods -----  
+  NS_IMETHOD HasTable( // supports a table with the specified oid?
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical table oid
+    mdb_bool* outHasTable) = 0; // whether GetTable() might succeed
+    
+  NS_IMETHOD GetTable( // access one table with specific oid
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical table oid
+    nsIMdbTable** acqTable) = 0; // acquire specific table (or null)
+  
+  NS_IMETHOD HasTableKind( // supports a table of the specified type?
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope, // rid scope for row ids
+    mdb_kind inTableKind, // the type of table to access
+    mdb_count* outTableCount, // current number of such tables
+    mdb_bool* outSupportsTable) = 0; // whether GetTableKind() might succeed
+    
+  // row scopes to be supported include the following suggestions:
+  // "ns:msg:db:row:scope:address:cards:all"
+  // "ns:msg:db:row:scope:mail:messages:all"
+  // "ns:msg:db:row:scope:news:articles:all"
+ 
+  // table kinds to be supported include the following suggestions:
+  // "ns:msg:db:table:kind:address:cards:main"
+  // "ns:msg:db:table:kind:address:lists:all" 
+  // "ns:msg:db:table:kind:address:list" 
+  // "ns:msg:db:table:kind:news:threads:all" 
+  // "ns:msg:db:table:kind:news:thread" 
+  // "ns:msg:db:table:kind:mail:threads:all"
+  // "ns:msg:db:table:kind:mail:thread"
+    
+  NS_IMETHOD GetTableKind( // access one (random) table of specific type
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope,      // row scope for row ids
+    mdb_kind inTableKind,      // the type of table to access
+    mdb_count* outTableCount, // current number of such tables
+    mdb_bool* outMustBeUnique, // whether port can hold only one of these
+    nsIMdbTable** acqTable) = 0;       // acquire scoped collection of rows
+    
+  NS_IMETHOD
+  GetPortTableCursor( // get cursor for all tables of specific type
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope, // row scope for row ids
+    mdb_kind inTableKind, // the type of table to access
+    nsIMdbPortTableCursor** acqCursor) = 0; // all such tables in the port
+  // } ----- end table methods -----
+
+
+  // { ----- begin commit methods -----
+
+  NS_IMETHOD ShouldCompress( // store wastes at least inPercentWaste?
+    nsIMdbEnv* ev, // context
+    mdb_percent inPercentWaste, // 0..100 percent file size waste threshold
+    mdb_percent* outActualWaste, // 0..100 percent of file actually wasted
+    mdb_bool* outShould) = 0; // true when about inPercentWaste% is wasted
+  // ShouldCompress() returns true if the store can determine that the file
+  // will shrink by an estimated percentage of inPercentWaste% (or more) if
+  // CompressCommit() is called, because that percentage of the file seems
+  // to be recoverable free space.  The granularity is only in terms of 
+  // percentage points, and any value over 100 is considered equal to 100.
+  //
+  // If a store only has an approximate idea how much space might be saved
+  // during a compress, then a best guess should be made.  For example, the
+  // Mork implementation might keep track of how much file space began with
+  // text content before the first updating transaction, and then consider
+  // all content following the start of the first transaction as potentially
+  // wasted space if it is all updates and not just new content.  (This is
+  // a safe assumption in the sense that behavior will stabilize on a low
+  // estimate of wastage after a commit removes all transaction updates.)
+  //
+  // Some db formats might attempt to keep a very accurate reckoning of free
+  // space size, so a very accurate determination can be made.  But other db
+  // formats might have difficulty determining size of free space, and might
+  // require some lengthy calculation to answer.  This is the reason for
+  // passing in the percentage threshold of interest, so that such lengthy
+  // computations can terminate early as soon as at least inPercentWaste is
+  // found, so that the entire file need not be groveled when unnecessary.
+  // However, we hope implementations will always favor fast but imprecise
+  // heuristic answers instead of extremely slow but very precise answers.
+  //
+  // If the outActualWaste parameter is non-nil, it will be used to return
+  // the actual estimated space wasted as a percentage of file size.  (This
+  // parameter is provided so callers need not call repeatedly with altered
+  // inPercentWaste values to isolate the actual wastage figure.)  Note the
+  // actual wastage figure returned can exactly equal inPercentWaste even
+  // when this grossly underestimates the real figure involved, if the db
+  // finds it very expensive to determine the extent of wastage after it is
+  // known to at least exceed inPercentWaste.  Note we expect that whenever
+  // outShould returns true, that outActualWaste returns >= inPercentWaste.
+  //
+  // The effect of different inPercentWaste values is not very uniform over
+  // the permitted range.  For example, 50 represents 50% wastage, or a file
+  // that is about double what it should be ideally.  But 99 represents 99%
+  // wastage, or a file that is about ninety-nine times as big as it should
+  // be ideally.  In the smaller direction, 25 represents 25% wastage, or
+  // a file that is only 33% larger than it should be ideally.
+  //
+  // Callers can determine what policy they want to use for considering when
+  // a file holds too much wasted space, and express this as a percentage
+  // of total file size to pass as in the inPercentWaste parameter.  A zero
+  // likely returns always trivially true, and 100 always trivially false.
+  // The great majority of callers are expected to use values from 25 to 75,
+  // since most plausible thresholds for compressing might fall between the
+  // extremes of 133% of ideal size and 400% of ideal size.  (Presumably the
+  // larger a file gets, the more important the percentage waste involved, so
+  // a sliding scale for compress thresholds might use smaller numbers for
+  // much bigger file sizes.)
+  
+  // } ----- end commit methods -----
+
+// } ===== end nsIMdbPort methods =====
+};
+
+/*| nsIMdbStore: a mutable interface to a specific database file.
+**|
+**|| tables: one can force a new table to exist in a store with NewTable()
+**| and nonzero values for both row scope and table kind.  (If one wishes only
+**| one table of a certain kind, then one might look for it first using the
+**| GetTableKind() method).  One can pass inMustBeUnique to force future
+**| users of this store to be unable to create other tables with the same pair
+**| of scope and kind attributes.  When inMustBeUnique is true, and the table
+**| with the given scope and kind pair already exists, then the existing one
+**| is returned instead of making a new table.  Similarly, if one passes false
+**| for inMustBeUnique, but the table kind has already been marked unique by a
+**| previous user of the store, then the existing unique table is returned.
+**|
+**|| import: all or some of another port's content can be imported by calling
+**| AddPortContent() with a row scope identifying the extent of content to
+**| be imported.  A zero row scope will import everything.  A nonzero row
+**| scope will only import tables with a matching row scope.  Note that one
+**| must somehow find a way to negotiate possible conflicts between existing
+**| row content and imported row content, and this involves a specific kind of
+**| definition for row identity involving either row IDs or unique attributes,
+**| or some combination of these two.  At the moment I am just going to wave
+**| my hands, and say the default behavior is to assign all new row identities
+**| to all imported content, which will result in no merging of content; this
+**| must change later because it is unacceptable in some contexts.
+**|
+**|| commits: to manage modifications in a mutable store, very few methods are
+**| really needed to indicate global policy choices that are independent of 
+**| the actual modifications that happen in objects at the level of tables,
+**| rows, and cells, etc.  The most important policy to specify is which sets
+**| of changes are considered associated in a manner such that they should be
+**| applied together atomically to a given store.  We call each such group of
+**| changes a transaction.  We handle three different grades of transaction,
+**| but they differ only in semantic significance to the application, and are
+**| not intended to nest.  (If small transactions were nested inside large
+**| transactions, that would imply that a single large transaction must be
+**| atomic over all the contained small transactions; but actually we intend
+**| smalls transaction never be undone once commited due to, say, aborting a
+**| transaction of greater significance.)  The small, large, and session level
+**| commits have equal granularity, and differ only in risk of loss from the
+**| perspective of an application.  Small commits characterize changes that
+**| can be lost with relatively small risk, so small transactions can delay
+**| until later if they are expensive or impractical to commit.  Large commits
+**| involve changes that would probably inconvenience users if lost, so the
+**| need to pay costs of writing is rather greater than with small commits.
+**| Session commits are last ditch attempts to save outstanding changes before
+**| stopping the use of a particular database, so there will be no later point
+**| in time to save changes that have been delayed due to possible high cost.
+**| If large commits are never delayed, then a session commit has about the
+**| same performance effect as another large commit; but if small and large
+**| commits are always delayed, then a session commit is likely to be rather
+**| expensive as a runtime cost compared to any earlier database usage.
+**|
+**|| aborts: the only way to abort changes to a store is by closing the store.
+**| So there is no specific method for causing any abort.  Stores must discard
+**| all changes made that are uncommited when a store is closed.  This design
+**| choice makes the implementations of tables, rows, and cells much less
+**| complex because they need not maintain a record of undobable changes.  When
+**| a store is closed, presumably this precipitates the closure of all tables,
+**| rows, and cells in the store as well.   So an application can revert the
+**| state of a store in the user interface by quietly closing and reopening a
+**| store, because this will discard uncommited changes and show old content.
+**| This implies an app that closes a store will need to send a "scramble"
+**| event notification to any views that depend on old discarded content.
+|*/
+
+#define NS_IMDBSTORE_IID_STR "726618d3-f15b-49b9-9f4a-efcc9db53d0d"
+
+#define NS_IMDBSTORE_IID \
+{0x726618d3, 0xf15b, 0x49b9, \
+{0x9f, 0x4a, 0xef, 0xcc, 0x9d, 0xb5, 0x3d, 0x0d}}
+
+class nsIMdbStore : public nsIMdbPort {
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBSTORE_IID)
+
+// { ===== begin nsIMdbStore methods =====
+
+  // { ----- begin table methods -----
+  NS_IMETHOD NewTable( // make one new table of specific type
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope,    // row scope for row ids
+    mdb_kind inTableKind,    // the type of table to access
+    mdb_bool inMustBeUnique, // whether store can hold only one of these
+    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+    nsIMdbTable** acqTable) = 0;     // acquire scoped collection of rows
+    
+  NS_IMETHOD NewTableWithOid( // make one new table of specific type
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,   // caller assigned oid
+    mdb_kind inTableKind,    // the type of table to access
+    mdb_bool inMustBeUnique, // whether store can hold only one of these
+    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+    nsIMdbTable** acqTable) = 0;     // acquire scoped collection of rows
+  // } ----- end table methods -----
+
+  // { ----- begin row scope methods -----
+  NS_IMETHOD RowScopeHasAssignedIds(nsIMdbEnv* ev,
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+    mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified
+
+  NS_IMETHOD SetCallerAssignedIds(nsIMdbEnv* ev,
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+    mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified
+
+  NS_IMETHOD SetStoreAssignedIds(nsIMdbEnv* ev,
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+    mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified
+  // } ----- end row scope methods -----
+
+  // { ----- begin row methods -----
+  NS_IMETHOD NewRowWithOid(nsIMdbEnv* ev, // new row w/ caller assigned oid
+    const mdbOid* inOid,   // caller assigned oid
+    nsIMdbRow** acqRow) = 0; // create new row
+
+  NS_IMETHOD NewRow(nsIMdbEnv* ev, // new row with db assigned oid
+    mdb_scope inRowScope,   // row scope for row ids
+    nsIMdbRow** acqRow) = 0; // create new row
+  // Note this row must be added to some table or cell child before the
+  // store is closed in order to make this row persist across sesssions.
+
+  // } ----- end row methods -----
+
+  // { ----- begin inport/export methods -----
+  NS_IMETHOD ImportContent( // import content from port
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope, // scope for rows (or zero for all?)
+    nsIMdbPort* ioPort, // the port with content to add to store
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental import
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the import will be finished.
+
+  NS_IMETHOD ImportFile( // import content from port
+    nsIMdbEnv* ev, // context
+    nsIMdbFile* ioFile, // the file with content to add to store
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental import
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the import will be finished.
+  // } ----- end inport/export methods -----
+
+  // { ----- begin hinting methods -----
+  NS_IMETHOD
+  ShareAtomColumnsHint( // advise re shared column content atomizing
+    nsIMdbEnv* ev, // context
+    mdb_scope inScopeHint, // zero, or suggested shared namespace
+    const mdbColumnSet* inColumnSet) = 0; // cols desired tokenized together
+
+  NS_IMETHOD
+  AvoidAtomColumnsHint( // advise column with poor atomizing prospects
+    nsIMdbEnv* ev, // context
+    const mdbColumnSet* inColumnSet) = 0; // cols with poor atomizing prospects
+  // } ----- end hinting methods -----
+
+  // { ----- begin commit methods -----
+  NS_IMETHOD SmallCommit( // save minor changes if convenient and uncostly
+    nsIMdbEnv* ev) = 0; // context
+  
+  NS_IMETHOD LargeCommit( // save important changes if at all possible
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the commit will be finished.  Note the store is effectively write
+  // locked until commit is finished or canceled through the thumb instance.
+  // Until the commit is done, the store will report it has readonly status.
+
+  NS_IMETHOD SessionCommit( // save all changes if large commits delayed
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the commit will be finished.  Note the store is effectively write
+  // locked until commit is finished or canceled through the thumb instance.
+  // Until the commit is done, the store will report it has readonly status.
+
+  NS_IMETHOD
+  CompressCommit( // commit and make db physically smaller if possible
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the commit will be finished.  Note the store is effectively write
+  // locked until commit is finished or canceled through the thumb instance.
+  // Until the commit is done, the store will report it has readonly status.
+  
+  // } ----- end commit methods -----
+
+// } ===== end nsIMdbStore methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbStore, NS_IMDBSTORE_IID)
+
+/*| nsIMdbCursor: base cursor class for iterating row cells and table rows
+**|
+**|| count: the number of elements in the collection (table or row)
+**|
+**|| seed: the change count in the underlying collection, which is synced
+**| with the collection when the iteration position is set, and henceforth
+**| acts to show whether the iter has lost collection synchronization, in
+**| case it matters to clients whether any change happens during iteration.
+**|
+**|| pos: the position of the current element in the collection.  Negative
+**| means a position logically before the first element.  A positive value
+**| equal to count (or larger) implies a position after the last element.
+**| To iterate over all elements, set the position to negative, so subsequent
+**| calls to any 'next' method will access the first collection element.
+**|
+**|| doFailOnSeedOutOfSync: whether a cursor should return an error if the
+**| cursor's snapshot of a table's seed becomes stale with respect the table's
+**| current seed value (which implies the iteration is less than total) in
+**| between to cursor calls that actually access collection content.  By
+**| default, a cursor should assume this attribute is false until specified,
+**| so that iterations quietly try to re-sync when they lose coherence.
+|*/
+
+#define NS_IMDBCURSOR_IID_STR "a0c37337-6ebc-474c-90db-e65ea0b850aa"
+
+#define NS_IMDBCURSOR_IID \
+{0xa0c37337, 0x6ebc, 0x474c, \
+{0x90, 0xdb, 0xe6, 0x5e, 0xa0, 0xb8, 0x50, 0xaa}}
+
+class nsIMdbCursor  : public nsISupports  { // collection iterator
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBCURSOR_IID)
+// { ===== begin nsIMdbCursor methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) = 0; // readonly
+  NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) = 0;    // readonly
+  
+  NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) = 0;   // mutable
+  NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) = 0;
+  
+  NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) = 0;
+  NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) = 0;
+  // } ----- end attribute methods -----
+
+// } ===== end nsIMdbCursor methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbCursor, NS_IMDBCURSOR_IID)
+
+#define NS_IMDBPORTTABLECURSOR_IID_STR = "f181a41e-933d-49b3-af93-20d3634b8b78"
+
+#define NS_IMDBPORTTABLECURSOR_IID \
+{0xf181a41e, 0x933d, 0x49b3, \
+{0xaf, 0x93, 0x20, 0xd3, 0x63, 0x4b, 0x8b, 0x78}}
+
+/*| nsIMdbPortTableCursor: cursor class for iterating port tables
+**|
+**|| port: the cursor is associated with a specific port, which can be
+**| set to a different port (which resets the position to -1 so the
+**| next table acquired is the first in the port.
+**|
+|*/
+class nsIMdbPortTableCursor : public nsISupports { // table collection iterator
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBPORTTABLECURSOR_IID)
+// { ===== begin nsIMdbPortTableCursor methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD SetPort(nsIMdbEnv* ev, nsIMdbPort* ioPort) = 0; // sets pos to -1
+  NS_IMETHOD GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort) = 0;
+  
+  NS_IMETHOD SetRowScope(nsIMdbEnv* ev, // sets pos to -1
+    mdb_scope inRowScope) = 0;
+  NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0; 
+  // setting row scope to zero iterates over all row scopes in port
+    
+  NS_IMETHOD SetTableKind(nsIMdbEnv* ev, // sets pos to -1
+    mdb_kind inTableKind) = 0;
+  NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0;
+  // setting table kind to zero iterates over all table kinds in row scope
+  // } ----- end attribute methods -----
+
+  // { ----- begin table iteration methods -----
+  NS_IMETHOD NextTable( // get table at next position in the db
+    nsIMdbEnv* ev, // context
+    nsIMdbTable** acqTable) = 0; // the next table in the iteration
+  // } ----- end table iteration methods -----
+
+// } ===== end nsIMdbPortTableCursor methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbPortTableCursor,
+                              NS_IMDBPORTTABLECURSOR_IID)
+
+/*| nsIMdbCollection: an object that collects a set of other objects as members.
+**| The main purpose of this base class is to unify the perceived semantics
+**| of tables and rows where their collection behavior is similar.  This helps
+**| isolate the mechanics of collection behavior from the other semantics that
+**| are more characteristic of rows and tables.
+**|
+**|| count: the number of objects in a collection is the member count. (Some
+**| collection interfaces call this attribute the 'size', but that can be a
+**| little ambiguous, and counting actual members is harder to confuse.)
+**|
+**|| seed: the seed of a collection is a counter for changes in membership in
+**| a specific collection.  This seed should change when members are added to
+**| or removed from a collection, but not when a member changes internal state.
+**| The seed should also change whenever the internal collection of members has
+**| a complex state change that reorders member positions (say by sorting) that
+**| would affect the nature of an iteration over that collection of members.
+**| The purpose of a seed is to inform any outstanding collection cursors that
+**| they might be stale, without incurring the cost of broadcasting an event
+**| notification to such cursors, which would need more data structure support.
+**| Presumably a cursor in a particular mdb code suite has much more direct
+**| access to a collection seed member slot that this abstract COM interface,
+**| so this information is intended more for clients outside mdb that want to
+**| make inferences similar to those made by the collection cursors.  The seed
+**| value as an integer magnitude is not very important, and callers should not
+**| assume meaningful information can be derived from an integer value beyond
+**| whether it is equal or different from a previous inspection.  A seed uses
+**| integers of many bits in order to make the odds of wrapping and becoming
+**| equal to an earlier seed value have probability that is vanishingly small.
+**|
+**|| port: every collection is associated with a specific database instance.
+**|
+**|| cursor: a subclass of nsIMdbCursor suitable for this specific collection
+**| subclass.  The ability to GetCursor() from the base nsIMdbCollection class
+**| is not really as useful as getting a more specifically typed cursor more
+**| directly from the base class without any casting involved.  So including
+**| this method here is more for conceptual illustration.
+**|
+**|| oid: every collection has an identity that persists from session to
+**| session. Implementations are probably able to distinguish row IDs from
+**| table IDs, but we don't specify anything official in this regard.  A
+**| collection has the same identity for the lifetime of the collection,
+**| unless identity is swapped with another collection by means of a call to
+**| BecomeContent(), which is considered a way to swap a new representation
+**| for an old well-known object.  (Even so, only content appears to change,
+**| while the identity seems to stay the same.)
+**|
+**|| become: developers can effectively cause two objects to swap identities,
+**| in order to effect a complete swap between what persistent content is
+**| represented by two oids.  The caller should consider this a content swap,
+**| and not identity wap, because identities will seem to stay the same while
+**| only content changes.  However, implementations will likely do this
+**| internally by swapping identities.  Callers must swap content only
+**| between objects of similar type, such as a row with another row, and a
+**| table with another table, because implementations need not support
+**| cross-object swapping because it might break object name spaces.
+**|
+**|| dropping: when a caller expects a row or table will no longer be used, the
+**| caller can tell the collection to 'drop activity', which means the runtime
+**| object can have its internal representation purged to save memory or any
+**| other resource that is being consumed by the collection's representation.
+**| This has no effect on the collection's persistent content or semantics,
+**| and is only considered a runtime effect.  After a collection drops
+**| activity, the object should still be as usable as before (because it has
+**| NOT been closed), but further usage can be expensive to re-instate because
+**| it might involve reallocating space and/or re-reading disk space.  But
+**| since this future usage is not expected, the caller does not expect to
+**| pay the extra expense.  An implementation can choose to implement
+**| 'dropping activity' in different ways, or even not at all if this
+**| operation is not really feasible.  Callers cannot ask objects whether they
+**| are 'dropped' or not, so this should be transparent. (Note that
+**| implementors might fear callers do not really know whether future
+**| usage will occur, and therefore might delay the act of dropping until
+**| the near future, until seeing whether the object is used again
+**| immediately elsewhere. Such use soon after the drop request might cause
+**| the drop to be cancelled.)
+|*/
+class nsIMdbCollection : public nsISupports { // sequence of objects
+public:
+
+// { ===== begin nsIMdbCollection methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetSeed(nsIMdbEnv* ev,
+    mdb_seed* outSeed) = 0;    // member change count
+  NS_IMETHOD GetCount(nsIMdbEnv* ev,
+    mdb_count* outCount) = 0; // member count
+
+  NS_IMETHOD GetPort(nsIMdbEnv* ev,
+    nsIMdbPort** acqPort) = 0; // collection container
+  // } ----- end attribute methods -----
+
+  // { ----- begin cursor methods -----
+  NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos
+    nsIMdbEnv* ev, // context
+    mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+    nsIMdbCursor** acqCursor) = 0; // acquire new cursor instance
+  // } ----- end cursor methods -----
+
+  // { ----- begin ID methods -----
+  NS_IMETHOD GetOid(nsIMdbEnv* ev,
+    mdbOid* outOid) = 0; // read object identity
+  NS_IMETHOD BecomeContent(nsIMdbEnv* ev,
+    const mdbOid* inOid) = 0; // exchange content
+  // } ----- end ID methods -----
+
+  // { ----- begin activity dropping methods -----
+  NS_IMETHOD DropActivity( // tell collection usage no longer expected
+    nsIMdbEnv* ev) = 0;
+  // } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+};
+
+/*| nsIMdbTable: an ordered collection of rows
+**|
+**|| row scope: an integer token for an atomized string in this database
+**| that names a space for row IDs.  This attribute of a table is intended
+**| as guidance metainformation that helps with searching a database for
+**| tables that operate on collections of rows of the specific type.  By
+**| convention, a table with a specific row scope is expected to focus on
+**| containing rows that belong to that scope, however exceptions are easily
+**| allowed because all rows in a table are known by both row ID and scope.
+**| (A table with zero row scope is never allowed because this would make it
+**| ambiguous to use a zero row scope when iterating over tables in a port to
+**| indicate that all row scopes should be seen by a cursor.)
+**|
+**|| table kind: an integer token for an atomized string in this database
+**| that names a kind of table as a subset of the associated row scope. This
+**| attribute is intended as guidance metainformation to clarify the role of
+**| this table with respect to other tables in the same row scope, and this
+**| also helps search for such tables in a database.  By convention, a table
+**| with a specific table kind has a consistent role for containing rows with
+**| respect to other collections of such rows in the same row scope.  Also by
+**| convention, at least one table in a row scope has a table kind purporting
+**| to contain ALL the rows that belong in that row scope, so that at least
+**| one table exists that allows all rows in a scope to be interated over.
+**| (A table with zero table kind is never allowed because this would make it
+**| ambiguous to use a zero table kind when iterating over tables in a port to
+**| indicate that all table kinds in a row scope should be seen by a cursor.)
+**|
+**|| port: every table is considered part of some port that contains the
+**| table, so that closing the containing port will cause the table to be
+**| indirectly closed as well.  We make it easy to get the containing port for
+**| a table, because the port supports important semantic interfaces that will
+**| affect how content in table is presented; the most important port context
+**| that affects a table is specified by the set of token to string mappings
+**| that affect all tokens used throughout the database, and which drive the
+**| meanings of row scope, table kind, cell columns, etc.
+**|
+**|| cursor: a cursor that iterates over the rows in this table, where rows
+**| have zero-based index positions from zero to count-1.  Making a cursor
+**| with negative position will next iterate over the first row in the table.
+**|
+**|| position: given any position from zero to count-1, a table will return
+**| the row ID and row scope for the row at that position.  (One can use the
+**| GetRowAllCells() method to read that row, or else use a row cursor to both
+**| get the row at some position and read its content at the same time.)  The
+**| position depends on whether a table is sorted, and upon the actual sort.
+**| Note that moving a row's position is only possible in unsorted tables.
+**|
+**|| row set: every table contains a collection of rows, where a member row is
+**| referenced by the table using the row ID and row scope for the row.  No
+**| single table owns a given row instance, because rows are effectively ref-
+**| counted and destroyed only when the last table removes a reference to that
+**| particular row.  (But a row can be emptied of all content no matter how
+**| many refs exist, and this might be the next best thing to destruction.)
+**| Once a row exists in a least one table (after NewRow() is called), then it
+**| can be added to any other table by calling AddRow(), or removed from any
+**| table by calling CutRow(), or queried as a member by calling HasRow().  A
+**| row can only be added to a table once, and further additions do nothing and
+**| complain not at all.  Cutting a row from a table only does something when
+**| the row was actually a member, and otherwise does nothing silently.
+**|
+**|| row ref count: one can query the number of tables (and/or cells)
+**| containing a row as a member or a child.
+**|
+**|| row content: one can access or modify the cell content in a table's row
+**| by moving content to or from an instance of nsIMdbRow.  Note that nsIMdbRow
+**| never represents the actual row inside a table, and this is the reason
+**| why nsIMdbRow instances do not have row IDs or row scopes.  So an instance
+**| of nsIMdbRow always and only contains a snapshot of some or all content in
+**| past, present, or future persistent row inside a table.  This means that
+**| reading and writing rows in tables has strictly copy semantics, and we
+**| currently do not plan any exceptions for specific performance reasons.
+**|
+**|| sorting: note all rows are assumed sorted by row ID as a secondary
+**| sort following the primary column sort, when table rows are sorted.
+**|
+**|| indexes:
+|*/
+
+
+#define NS_IMDBTABLE_IID_STR = "fe11bc98-d02b-4128-9fac-87042fdf9639"
+
+#define NS_IMDBTABLE_IID \
+{0xfe11bc98, 0xd02b, 0x4128, \
+{0x9f, 0xac, 0x87, 0x04, 0x2f, 0xdf, 0x96, 0x39}}
+
+class nsIMdbTable : public nsIMdbCollection { // a collection of rows
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLE_IID)
+// { ===== begin nsIMdbTable methods =====
+
+  // { ----- begin meta attribute methods -----
+  NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) = 0;
+  NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) = 0;
+  
+  NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) = 0;
+  NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) = 0;
+  
+  NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) = 0;
+  
+  NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0;
+  NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0;
+  
+  NS_IMETHOD GetMetaRow(
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+    mdbOid* outOid, // output meta row oid, can be nil to suppress output
+    nsIMdbRow** acqRow) = 0; // acquire table's unique singleton meta row
+    // The purpose of a meta row is to support the persistent recording of
+    // meta info about a table as cells put into the distinguished meta row.
+    // Each table has exactly one meta row, which is not considered a member
+    // of the collection of rows inside the table.  The only way to tell
+    // whether a row is a meta row is by the fact that it is returned by this
+    // GetMetaRow() method from some table. Otherwise nothing distinguishes
+    // a meta row from any other row.  A meta row can be used anyplace that
+    // any other row can be used, and can even be put into other tables (or
+    // the same table) as a table member, if this is useful for some reason.
+    // The first attempt to access a table's meta row using GetMetaRow() will
+    // cause the meta row to be created if it did not already exist.  When the
+    // meta row is created, it will have the row oid that was previously
+    // requested for this table's meta row; or if no oid was ever explicitly
+    // specified for this meta row, then a unique oid will be generated in
+    // the row scope named "m" (so obviously MDB clients should not
+    // manually allocate any row IDs from that special meta scope namespace).
+    // The meta row oid can be specified either when the table is created, or
+    // else the first time that GetMetaRow() is called, by passing a non-nil
+    // pointer to an oid for parameter inOptionalMetaRowOid.  The meta row's
+    // actual oid is returned in outOid (if this is a non-nil pointer), and
+    // it will be different from inOptionalMetaRowOid when the meta row was
+    // already given a different oid earlier.
+  // } ----- end meta attribute methods -----
+
+
+  // { ----- begin cursor methods -----
+  NS_IMETHOD GetTableRowCursor( // make a cursor, starting iteration at inRowPos
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance
+  // } ----- end row position methods -----
+
+  // { ----- begin row position methods -----
+  NS_IMETHOD PosToOid( // get row member for a table position
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    mdbOid* outOid) = 0; // row oid at the specified position
+
+  NS_IMETHOD OidToPos( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid, // row to find in table
+    mdb_pos* outPos) = 0; // zero-based ordinal position of row in table
+    
+  NS_IMETHOD PosToRow( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos
+    
+  NS_IMETHOD RowToPos( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow, // row to find in table
+    mdb_pos* outPos) = 0; // zero-based ordinal position of row in table
+  // } ----- end row position methods -----
+
+  // { ----- begin oid set methods -----
+  NS_IMETHOD AddOid( // make sure the row with inOid is a table member 
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid) = 0; // row to ensure membership in table
+
+  NS_IMETHOD HasOid( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid, // row to find in table
+    mdb_bool* outHasOid) = 0; // whether inOid is a member row
+
+  NS_IMETHOD CutOid( // make sure the row with inOid is not a member 
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid) = 0; // row to remove from table
+  // } ----- end oid set methods -----
+
+  // { ----- begin row set methods -----
+  NS_IMETHOD NewRow( // create a new row instance in table
+    nsIMdbEnv* ev, // context
+    mdbOid* ioOid, // please use minus one (unbound) rowId for db-assigned IDs
+    nsIMdbRow** acqRow) = 0; // create new row
+
+  NS_IMETHOD AddRow( // make sure the row with inOid is a table member 
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow) = 0; // row to ensure membership in table
+
+  NS_IMETHOD HasRow( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow, // row to find in table
+    mdb_bool* outHasRow) = 0; // whether row is a table member
+
+  NS_IMETHOD CutRow( // make sure the row with inOid is not a member 
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow) = 0; // row to remove from table
+
+  NS_IMETHOD CutAllRows( // remove all rows from the table
+    nsIMdbEnv* ev) = 0; // context
+  // } ----- end row set methods -----
+
+  // { ----- begin hinting methods -----
+  NS_IMETHOD SearchColumnsHint( // advise re future expected search cols  
+    nsIMdbEnv* ev, // context
+    const mdbColumnSet* inColumnSet) = 0; // columns likely to be searched
+    
+  NS_IMETHOD SortColumnsHint( // advise re future expected sort columns  
+    nsIMdbEnv* ev, // context
+    const mdbColumnSet* inColumnSet) = 0; // columns for likely sort requests
+    
+  NS_IMETHOD StartBatchChangeHint( // advise before many adds and cuts  
+    nsIMdbEnv* ev, // context
+    const void* inLabel) = 0; // intend unique address to match end call
+    // If batch starts nest by virtue of nesting calls in the stack, then
+    // the address of a local variable makes a good batch start label that
+    // can be used at batch end time, and such addresses remain unique.
+    
+  NS_IMETHOD EndBatchChangeHint( // advise before many adds and cuts  
+    nsIMdbEnv* ev, // context
+    const void* inLabel) = 0; // label matching start label
+    // Suppose a table is maintaining one or many sort orders for a table,
+    // so that every row added to the table must be inserted in each sort,
+    // and every row cut must be removed from each sort.  If a db client
+    // intends to make many such changes before needing any information
+    // about the order or positions of rows inside a table, then a client
+    // might tell the table to start batch changes in order to disable
+    // sorting of rows for the interim.  Presumably a table will then do
+    // a full sort of all rows at need when the batch changes end, or when
+    // a surprise request occurs for row position during batch changes.
+  // } ----- end hinting methods -----
+
+  // { ----- begin searching methods -----
+  NS_IMETHOD FindRowMatches( // search variable number of sorted cols
+    nsIMdbEnv* ev, // context
+    const mdbYarn* inPrefix, // content to find as prefix in row's column cell
+    nsIMdbTableRowCursor** acqCursor) = 0; // set of matching rows
+    
+  NS_IMETHOD GetSearchColumns( // query columns used by FindRowMatches()
+    nsIMdbEnv* ev, // context
+    mdb_count* outCount, // context
+    mdbColumnSet* outColSet) = 0; // caller supplied space to put columns
+    // GetSearchColumns() returns the columns actually searched when the
+    // FindRowMatches() method is called.  No more than mColumnSet_Count
+    // slots of mColumnSet_Columns will be written, since mColumnSet_Count
+    // indicates how many slots are present in the column array.  The
+    // actual number of search column used by the table is returned in
+    // the outCount parameter; if this number exceeds mColumnSet_Count,
+    // then a caller needs a bigger array to read the entire column set.
+    // The minimum of mColumnSet_Count and outCount is the number slots
+    // in mColumnSet_Columns that were actually written by this method.
+    //
+    // Callers are expected to change this set of columns by calls to
+    // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
+  // } ----- end searching methods -----
+
+  // { ----- begin sorting methods -----
+  // sorting: note all rows are assumed sorted by row ID as a secondary
+  // sort following the primary column sort, when table rows are sorted.
+
+  NS_IMETHOD
+  CanSortColumn( // query which column is currently used for sorting
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // column to query sorting potential
+    mdb_bool* outCanSort) = 0; // whether the column can be sorted
+    
+  NS_IMETHOD GetSorting( // view same table in particular sorting
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // requested new column for sorting table
+    nsIMdbSorting** acqSorting) = 0; // acquire sorting for column
+    
+  NS_IMETHOD SetSearchSorting( // use this sorting in FindRowMatches()
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
+    nsIMdbSorting* ioSorting) = 0; // requested sorting for some column
+    // SetSearchSorting() attempts to inform the table that ioSorting
+    // should be used during calls to FindRowMatches() for searching
+    // the column which is actually sorted by ioSorting.  This method
+    // is most useful in conjunction with nsIMdbSorting::SetCompare(),
+    // because otherwise a caller would not be able to override the
+    // comparison ordering method used during searchs.  Note that some
+    // database implementations might be unable to use an arbitrarily
+    // specified sort order, either due to schema or runtime interface
+    // constraints, in which case ioSorting might not actually be used.
+    // Presumably ioSorting is an instance that was returned from some
+    // earlier call to nsIMdbTable::GetSorting().  A caller can also
+    // use nsIMdbTable::SearchColumnsHint() to specify desired change
+    // in which columns are sorted and searched by FindRowMatches().
+    //
+    // A caller can pass a nil pointer for ioSorting to request that
+    // column inColumn no longer be used at all by FindRowMatches().
+    // But when ioSorting is non-nil, then inColumn should match the
+    // column actually sorted by ioSorting; when these do not agree,
+    // implementations are instructed to give precedence to the column
+    // specified by ioSorting (so this means callers might just pass
+    // zero for inColumn when ioSorting is also provided, since then
+    // inColumn is both redundant and ignored).
+  // } ----- end sorting methods -----
+
+  // { ----- begin moving methods -----
+  // moving a row does nothing unless a table is currently unsorted
+  
+  NS_IMETHOD MoveOid( // change position of row in unsorted table
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // row oid to find in table
+    mdb_pos inHintFromPos, // suggested hint regarding start position
+    mdb_pos inToPos,       // desired new position for row inRowId
+    mdb_pos* outActualPos) = 0; // actual new position of row in table
+
+  NS_IMETHOD MoveRow( // change position of row in unsorted table
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow,  // row oid to find in table
+    mdb_pos inHintFromPos, // suggested hint regarding start position
+    mdb_pos inToPos,       // desired new position for row inRowId
+    mdb_pos* outActualPos) = 0; // actual new position of row in table
+  // } ----- end moving methods -----
+  
+  // { ----- begin index methods -----
+  NS_IMETHOD AddIndex( // create a sorting index for column if possible
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // the column to sort by index
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental index building
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the index addition will be finished.
+  
+  NS_IMETHOD CutIndex( // stop supporting a specific column index
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // the column with index to be removed
+    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental index destroy
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the index removal will be finished.
+  
+  NS_IMETHOD HasIndex( // query for current presence of a column index
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // the column to investigate
+    mdb_bool* outHasIndex) = 0; // whether column has index for this column
+
+  
+  NS_IMETHOD EnableIndexOnSort( // create an index for col on first sort
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn) = 0; // the column to index if ever sorted
+  
+  NS_IMETHOD QueryIndexOnSort( // check whether index on sort is enabled
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // the column to investigate
+    mdb_bool* outIndexOnSort) = 0; // whether column has index-on-sort enabled
+  
+  NS_IMETHOD DisableIndexOnSort( // prevent future index creation on sort
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn) = 0; // the column to index if ever sorted
+  // } ----- end index methods -----
+
+// } ===== end nsIMdbTable methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbTable, NS_IMDBTABLE_IID)
+
+/*| nsIMdbSorting: a view of a table in some particular sort order.  This
+**| row order closely resembles a readonly array of rows with the same row
+**| membership as the underlying table, but in a different order than the
+**| table's explicit row order.  But the sorting's row membership changes
+**| whenever the table's membership changes (without any notification, so
+**| keep this in mind when modifying the table).
+**|
+**|| table: every sorting is associated with a particular table.  You
+**| cannot change which table is used by a sorting (just ask some new
+**| table for a suitable sorting instance instead).
+**|
+**|| compare: the ordering method used by a sorting, wrapped up in a
+**| abstract plug-in interface.  When this was never installed by an
+**| explicit call to SetNewCompare(), a compare object is still returned,
+**| and it might match the compare instance returned by the factory method
+**| nsIMdbFactory::MakeCompare(), which represents a default sort order
+**| (which we fervently hope is consistently ASCII byte ordering).
+**|
+**|| cursor: in case callers are more comfortable with a cursor style
+**| of accessing row members, each sorting will happily return a cursor
+**| instance with behavior very similar to a cursor returned from a call
+**| to nsIMdbTable::GetTableRowCursor(), but with different row order.
+**| A cursor should show exactly the same information as the pos methods.
+**|
+**|| pos: the PosToOid() and PosToRow() methods are just like the table
+**| methods of the same name, except they show rows in the sort order of 
+**| the sorting, rather than that of the table.  These methods are like
+**| readonly array position accessor's, or like a C++ operator[].
+|*/
+class nsIMdbSorting : public nsIMdbObject { // sorting of some table
+public:
+// { ===== begin nsIMdbSorting methods =====
+
+  // { ----- begin attribute methods -----
+  // sorting: note all rows are assumed sorted by row ID as a secondary
+  // sort following the primary column sort, when table rows are sorted.
+  
+  NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0;
+  NS_IMETHOD GetSortColumn( // query which col is currently sorted
+    nsIMdbEnv* ev, // context
+    mdb_column* outColumn) = 0; // col the table uses for sorting (or zero)
+
+  NS_IMETHOD SetNewCompare(nsIMdbEnv* ev,
+    nsIMdbCompare* ioNewCompare) = 0;
+    // Setting the sorting's compare object will typically cause the rows
+    // to be resorted, presumably in a lazy fashion when the sorting is
+    // next required to be in a valid row ordering state, such as when a
+    // call to PosToOid() happens.  ioNewCompare can be nil, in which case
+    // implementations should revert to the default sort order, which must
+    // be equivalent to whatever is used by nsIMdbFactory::MakeCompare().
+
+  NS_IMETHOD GetOldCompare(nsIMdbEnv* ev,
+    nsIMdbCompare** acqOldCompare) = 0;
+    // Get this sorting instance's compare object, which handles the
+    // ordering of rows in the sorting, by comparing yarns from the cells
+    // in the column being sorted.  Since nsIMdbCompare has no interface
+    // to query the state of the compare object, it is not clear what you
+    // would do with this object when returned, except maybe compare it
+    // as a pointer address to some other instance, to check identities.
+  
+  // } ----- end attribute methods -----
+
+  // { ----- begin cursor methods -----
+  NS_IMETHOD GetSortingRowCursor( // make a cursor, starting at inRowPos
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance
+    // A cursor interface turning same info as PosToOid() or PosToRow().
+  // } ----- end row position methods -----
+
+  // { ----- begin row position methods -----
+  NS_IMETHOD PosToOid( // get row member for a table position
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    mdbOid* outOid) = 0; // row oid at the specified position
+    
+  NS_IMETHOD PosToRow( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos
+  // } ----- end row position methods -----
+
+// } ===== end nsIMdbSorting methods =====
+};
+
+/*| nsIMdbTableRowCursor: cursor class for iterating table rows
+**|
+**|| table: the cursor is associated with a specific table, which can be
+**| set to a different table (which resets the position to -1 so the
+**| next row acquired is the first in the table.
+**|
+**|| NextRowId: the rows in the table can be iterated by identity alone,
+**| without actually reading the cells of any row with this method.
+**|
+**|| NextRowCells: read the next row in the table, but only read cells
+**| from the table which are already present in the row (so no new cells
+**| are added to the row, even if they are present in the table).  All the
+**| cells will have content specified, even it is the empty string.  No
+**| columns will be removed, even if missing from the row (because missing
+**| and empty are semantically equivalent).
+**|
+**|| NextRowAllCells: read the next row in the table, and access all the
+**| cells for this row in the table, adding any missing columns to the row
+**| as needed until all cells are represented.  All the
+**| cells will have content specified, even it is the empty string.  No
+**| columns will be removed, even if missing from the row (because missing
+**| and empty are semantically equivalent).
+**|
+|*/
+
+#define NS_IMDBTABLEROWCURSOR_IID_STR = "4f325dad-0385-4b62-a992-c914ab93587e"
+
+#define NS_IMDBTABLEROWCURSOR_IID \
+{0x4f325dad, 0x0385, 0x4b62, \
+{0xa9, 0x92, 0xc9, 0x14, 0xab, 0x93, 0x58, 0x7e}}
+
+
+
+class nsIMdbTableRowCursor : public nsISupports { // table row iterator
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLEROWCURSOR_IID)
+
+// { ===== begin nsIMdbTableRowCursor methods =====
+
+  // { ----- begin attribute methods -----
+  // NS_IMETHOD SetTable(nsIMdbEnv* ev, nsIMdbTable* ioTable) = 0; // sets pos to -1
+  // Method SetTable() cut and made obsolete in keeping with new sorting methods.
+  
+  NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0;
+  // } ----- end attribute methods -----
+
+  // { ----- begin duplicate row removal methods -----
+  NS_IMETHOD CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups?
+    mdb_bool* outCanHaveDups) = 0;
+    
+  NS_IMETHOD MakeUniqueCursor( // clone cursor, removing duplicate rows
+    nsIMdbEnv* ev, // context
+    nsIMdbTableRowCursor** acqCursor) = 0;    // acquire clone with no dups
+    // Note that MakeUniqueCursor() is never necessary for a cursor which was
+    // created by table method nsIMdbTable::GetTableRowCursor(), because a table
+    // never contains the same row as a member more than once.  However, a cursor
+    // created by table method nsIMdbTable::FindRowMatches() might contain the
+    // same row more than once, because the same row can generate a hit by more
+    // than one column with a matching string prefix.  Note this method can
+    // return the very same cursor instance with just an incremented refcount,
+    // when the original cursor could not contain any duplicate rows (calling
+    // CanHaveDupRowMembers() shows this case on a false return).  Otherwise
+    // this method returns a different cursor instance.  Callers should not use
+    // this MakeUniqueCursor() method lightly, because it tends to defeat the
+    // purpose of lazy programming techniques, since it can force creation of
+    // an explicit row collection in a new cursor's representation, in order to
+    // inspect the row membership and remove any duplicates; this can have big
+    // impact if a collection holds tens of thousands of rows or more, when
+    // the original cursor with dups simply referenced rows indirectly by row
+    // position ranges, without using an explicit row set representation.
+    // Callers are encouraged to use nsIMdbCursor::GetCount() to determine
+    // whether the row collection is very large (tens of thousands), and to
+    // delay calling MakeUniqueCursor() when possible, until a user interface
+    // element actually demands the creation of an explicit set representation.
+  // } ----- end duplicate row removal methods -----
+
+  // { ----- begin oid iteration methods -----
+  NS_IMETHOD NextRowOid( // get row id of next row in the table
+    nsIMdbEnv* ev, // context
+    mdbOid* outOid, // out row oid
+    mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
+  // } ----- end oid iteration methods -----
+
+  // { ----- begin row iteration methods -----
+  NS_IMETHOD NextRow( // get row cells from table for cells already in row
+    nsIMdbEnv* ev, // context
+    nsIMdbRow** acqRow, // acquire next row in table
+    mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
+
+  NS_IMETHOD PrevRowOid( // get row id of previous row in the table
+    nsIMdbEnv* ev, // context
+    mdbOid* outOid, // out row oid
+    mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
+  // } ----- end oid iteration methods -----
+
+  // { ----- begin row iteration methods -----
+  NS_IMETHOD PrevRow( // get row cells from table for cells already in row
+    nsIMdbEnv* ev, // context
+    nsIMdbRow** acqRow, // acquire previous row in table
+    mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
+
+  // } ----- end row iteration methods -----
+
+  // { ----- begin copy iteration methods -----
+  // NS_IMETHOD NextRowCopy( // put row cells into sink only when already in sink
+  //   nsIMdbEnv* ev, // context
+  //   nsIMdbRow* ioSinkRow, // sink for row cells read from next row
+  //   mdbOid* outOid, // out row oid
+  //   mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
+  // 
+  // NS_IMETHOD NextRowCopyAll( // put all row cells into sink, adding to sink
+  //   nsIMdbEnv* ev, // context
+  //   nsIMdbRow* ioSinkRow, // sink for row cells read from next row
+  //   mdbOid* outOid, // out row oid
+  //   mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
+  // } ----- end copy iteration methods -----
+
+// } ===== end nsIMdbTableRowCursor methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbTableRowCursor, NS_IMDBTABLEROWCURSOR_IID)
+
+/*| nsIMdbRow: a collection of cells
+**|
+|*/
+
+#define NS_IMDBROW_IID_STR "271e8d6e-183a-40e3-9f18-36913b4c7853"
+
+
+#define NS_IMDBROW_IID \
+{0x271e8d6e, 0x183a, 0x40e3, \
+{0x9f, 0x18, 0x36, 0x91, 0x3b, 0x4c, 0x78, 0x53}}
+
+
+class nsIMdbRow : public nsIMdbCollection { // cell tuple
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBROW_IID)
+// { ===== begin nsIMdbRow methods =====
+
+  // { ----- begin cursor methods -----
+  NS_IMETHOD GetRowCellCursor( // make a cursor starting iteration at inCellPos
+    nsIMdbEnv* ev, // context
+    mdb_pos inCellPos, // zero-based ordinal position of cell in row
+    nsIMdbRowCellCursor** acqCursor) = 0; // acquire new cursor instance
+  // } ----- end cursor methods -----
+
+  // { ----- begin column methods -----
+  NS_IMETHOD AddColumn( // make sure a particular column is inside row
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // column to add
+    const mdbYarn* inYarn) = 0; // cell value to install
+
+  NS_IMETHOD CutColumn( // make sure a column is absent from the row
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn) = 0; // column to ensure absent from row
+
+  NS_IMETHOD CutAllColumns( // remove all columns from the row
+    nsIMdbEnv* ev) = 0; // context
+  // } ----- end column methods -----
+
+  // { ----- begin cell methods -----
+  NS_IMETHOD NewCell( // get cell for specified column, or add new one
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // column to add
+    nsIMdbCell** acqCell) = 0; // cell column and value
+    
+  NS_IMETHOD AddCell( // copy a cell from another row to this row
+    nsIMdbEnv* ev, // context
+    const nsIMdbCell* inCell) = 0; // cell column and value
+    
+  NS_IMETHOD GetCell( // find a cell in this row
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // column to find
+    nsIMdbCell** acqCell) = 0; // cell for specified column, or null
+    
+  NS_IMETHOD EmptyAllCells( // make all cells in row empty of content
+    nsIMdbEnv* ev) = 0; // context
+  // } ----- end cell methods -----
+
+  // { ----- begin row methods -----
+  NS_IMETHOD AddRow( // add all cells in another row to this one
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioSourceRow) = 0; // row to union with
+    
+  NS_IMETHOD SetRow( // make exact duplicate of another row
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioSourceRow) = 0; // row to duplicate
+  // } ----- end row methods -----
+
+  // { ----- begin blob methods -----  
+  NS_IMETHOD SetCellYarn(nsIMdbEnv* ev, // synonym for AddColumn()
+    mdb_column inColumn, // column to write
+    const mdbYarn* inYarn) = 0;   // reads from yarn slots
+  // make this text object contain content from the yarn's buffer
+  
+  NS_IMETHOD GetCellYarn(nsIMdbEnv* ev, 
+    mdb_column inColumn, // column to read 
+    mdbYarn* outYarn) = 0;  // writes some yarn slots 
+  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+  
+  NS_IMETHOD AliasCellYarn(nsIMdbEnv* ev, 
+    mdb_column inColumn, // column to alias
+    mdbYarn* outYarn) = 0; // writes ALL yarn slots
+  
+  NS_IMETHOD NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn()
+    mdb_column* ioColumn, // next column to read
+    mdbYarn* outYarn) = 0;  // writes some yarn slots 
+  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+  //
+  // The ioColumn argument is an inout parameter which initially contains the
+  // last column accessed and returns the next column corresponding to the
+  // content read into the yarn.  Callers should start with a zero column
+  // value to say 'no previous column', which causes the first column to be
+  // read.  Then the value returned in ioColumn is perfect for the next call
+  // to NextCellYarn(), since it will then be the previous column accessed.
+  // Callers need only examine the column token returned to see which cell
+  // in the row is being read into the yarn.  When no more columns remain,
+  // and the iteration has ended, ioColumn will return a zero token again.
+  // So iterating over cells starts and ends with a zero column token.
+
+  NS_IMETHOD SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell()
+    nsIMdbEnv* ev, // context
+    mdb_pos inPos, // position of cell in row sequence
+    mdb_column* outColumn, // column for this particular cell
+    mdbYarn* outYarn) = 0; // writes some yarn slots
+  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+  // Callers can pass nil for outYarn to indicate no interest in content, so
+  // only the outColumn value is returned.  NOTE to subclasses: you must be
+  // able to ignore outYarn when the pointer is nil; please do not crash.
+
+  // } ----- end blob methods -----
+
+// } ===== end nsIMdbRow methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbRow, NS_IMDBROW_IID)
+
+/*| nsIMdbRowCellCursor: cursor class for iterating row cells
+**|
+**|| row: the cursor is associated with a specific row, which can be
+**| set to a different row (which resets the position to -1 so the
+**| next cell acquired is the first in the row.
+**|
+**|| NextCell: get the next cell in the row and return its position and
+**| a new instance of a nsIMdbCell to represent this next cell.
+|*/
+
+#define NS_IMDBROWCELLCURSOR_IID_STR "b33371a7-5d63-4d10-85a8-e44dffe75c28"
+
+
+#define NS_IMDBROWCELLCURSOR_IID \
+{0x271e8d6e, 0x5d63, 0x4d10 , \
+{0x85, 0xa8, 0xe4, 0x4d, 0xff, 0xe7, 0x5c, 0x28}}
+
+
+class nsIMdbRowCellCursor : public nsISupports{ // cell collection iterator
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBROWCELLCURSOR_IID)
+// { ===== begin nsIMdbRowCellCursor methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD SetRow(nsIMdbEnv* ev, nsIMdbRow* ioRow) = 0; // sets pos to -1
+  NS_IMETHOD GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow) = 0;
+  // } ----- end attribute methods -----
+
+  // { ----- begin cell creation methods -----
+  NS_IMETHOD MakeCell( // get cell at current pos in the row
+    nsIMdbEnv* ev, // context
+    mdb_column* outColumn, // column for this particular cell
+    mdb_pos* outPos, // position of cell in row sequence
+    nsIMdbCell** acqCell) = 0; // the cell at inPos
+  // } ----- end cell creation methods -----
+
+  // { ----- begin cell seeking methods -----
+  NS_IMETHOD SeekCell( // same as SetRow() followed by MakeCell()
+    nsIMdbEnv* ev, // context
+    mdb_pos inPos, // position of cell in row sequence
+    mdb_column* outColumn, // column for this particular cell
+    nsIMdbCell** acqCell) = 0; // the cell at inPos
+  // } ----- end cell seeking methods -----
+
+  // { ----- begin cell iteration methods -----
+  NS_IMETHOD NextCell( // get next cell in the row
+    nsIMdbEnv* ev, // context
+    nsIMdbCell** acqCell, // changes to the next cell in the iteration
+    mdb_column* outColumn, // column for this particular cell
+    mdb_pos* outPos) = 0; // position of cell in row sequence
+    
+  NS_IMETHOD PickNextCell( // get next cell in row within filter set
+    nsIMdbEnv* ev, // context
+    nsIMdbCell* ioCell, // changes to the next cell in the iteration
+    const mdbColumnSet* inFilterSet, // col set of actual caller interest
+    mdb_column* outColumn, // column for this particular cell
+    mdb_pos* outPos) = 0; // position of cell in row sequence
+
+  // Note that inFilterSet should not have too many (many more than 10?)
+  // cols, since this might imply a potential excessive consumption of time
+  // over many cursor calls when looking for column and filter intersection.
+  // } ----- end cell iteration methods -----
+
+// } ===== end nsIMdbRowCellCursor methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbRowCellCursor, NS_IMDBROWCELLCURSOR_IID)
+
+/*| nsIMdbBlob: a base class for objects composed mainly of byte sequence state.
+**| (This provides a base class for nsIMdbCell, so that cells themselves can
+**| be used to set state in another cell, without extracting a buffer.)
+|*/
+class nsIMdbBlob : public nsISupports { // a string with associated charset
+public:
+
+// { ===== begin nsIMdbBlob methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD SetBlob(nsIMdbEnv* ev,
+    nsIMdbBlob* ioBlob) = 0; // reads inBlob slots
+  // when inBlob is in the same suite, this might be fastest cell-to-cell
+  
+  NS_IMETHOD ClearBlob( // make empty (so content has zero length)
+    nsIMdbEnv* ev) = 0;
+  // clearing a yarn is like SetYarn() with empty yarn instance content
+  
+  NS_IMETHOD GetBlobFill(nsIMdbEnv* ev,
+    mdb_fill* outFill) = 0;  // size of blob 
+  // Same value that would be put into mYarn_Fill, if one called GetYarn()
+  // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0.
+  
+  NS_IMETHOD SetYarn(nsIMdbEnv* ev, 
+    const mdbYarn* inYarn) = 0;   // reads from yarn slots
+  // make this text object contain content from the yarn's buffer
+  
+  NS_IMETHOD GetYarn(nsIMdbEnv* ev, 
+    mdbYarn* outYarn) = 0;  // writes some yarn slots 
+  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+  
+  NS_IMETHOD AliasYarn(nsIMdbEnv* ev, 
+    mdbYarn* outYarn) = 0; // writes ALL yarn slots
+  // AliasYarn() reveals sensitive internal text buffer state to the caller
+  // by setting mYarn_Buf to point into the guts of this text implementation.
+  //
+  // The caller must take great care to avoid writing on this space, and to
+  // avoid calling any method that would cause the state of this text object
+  // to change (say by directly or indirectly setting the text to hold more
+  // content that might grow the size of the buffer and free the old buffer).
+  // In particular, callers should scrupulously avoid making calls into the
+  // mdb interface to write any content while using the buffer pointer found
+  // in the returned yarn instance.  Best safe usage involves copying content
+  // into some other kind of external content representation beyond mdb.
+  //
+  // (The original design of this method a week earlier included the concept
+  // of very fast and efficient cooperative locking via a pointer to some lock
+  // member slot.  But let's ignore that complexity in the current design.)
+  //
+  // AliasYarn() is specifically intended as the first step in transferring
+  // content from nsIMdbBlob to a nsString representation, without forcing extra
+  // allocations and/or memory copies. (A standard nsIMdbBlob_AsString() utility
+  // will use AliasYarn() as the first step in setting a nsString instance.)
+  //
+  // This is an alternative to the GetYarn() method, which has copy semantics
+  // only; AliasYarn() relaxes a robust safety principle only for performance
+  // reasons, to accomodate the need for callers to transform text content to
+  // some other canonical representation that would necessitate an additional
+  // copy and transformation when such is incompatible with the mdbYarn format.
+  //
+  // The implementation of AliasYarn() should have extremely little overhead
+  // besides the virtual dispatch to the method implementation, and the code
+  // necessary to populate all the mdbYarn member slots with internal buffer
+  // address and metainformation that describes the buffer content.  Note that
+  // mYarn_Grow must always be set to nil to indicate no resizing is allowed.
+  
+  // } ----- end attribute methods -----
+
+// } ===== end nsIMdbBlob methods =====
+};
+
+/*| nsIMdbCell: the text in a single column of a row.  The base nsIMdbBlob
+**| class provides all the interface related to accessing cell text.
+**|
+**|| column: each cell in a row appears in a specific column, where this
+**| column is identified by the an integer mdb_scope value (generated by
+**| the StringToScopeToken() method in the containing nsIMdbPort instance).
+**| Because a row cannot have more than one cell with the same column,
+**| something must give if one calls SetColumn() with an existing column
+**| in the same row. When this happens, the other cell is replaced with
+**| this cell (and the old cell is closed if it has outstanding refs).
+**|
+**|| row: every cell instance is a part of some row, and every cell knows
+**| which row is the parent row.  (Note this should be represented by a
+**| weak backpointer, so that outstanding cell references cannot keep a
+**| row open that should be closed. Otherwise we'd have ref graph cycles.)
+**|
+**|| text: a cell can either be text, or it can have a child row or table,
+**| but not both at once.  If text is read from a cell with a child, the text
+**| content should be empty (for AliasYarn()) or a description of the type
+**| of child (perhaps "mdb:cell:child:row" or "mdb:cell:child:table").
+**|
+**|| child: a cell might reference another row or a table, rather than text.
+**| The interface for putting and getting children rows and tables was first
+**| defined in the nsIMdbTable interface, but then this was moved to this cell
+**| interface as more natural. 
+|*/
+
+
+
+#define NS_IMDBCELL_IID \
+{0xa3b62f71, 0xa181, 0x4a91, \
+{0xb6, 0x6b, 0x27, 0x10, 0x9b, 0x88, 0x98, 0x35}}
+
+#define NS_IMDBCELL_IID_STR = "a3b62f71-a181-4a91-b66b-27109b889835"
+
+class nsIMdbCell : public nsIMdbBlob { // text attribute in row with column scope
+public:
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLEROWCURSOR_IID)
+// { ===== begin nsIMdbCell methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD SetColumn(nsIMdbEnv* ev, mdb_column inColumn) = 0; 
+  NS_IMETHOD GetColumn(nsIMdbEnv* ev, mdb_column* outColumn) = 0;
+  
+  NS_IMETHOD GetCellInfo(  // all cell metainfo except actual content
+    nsIMdbEnv* ev, 
+    mdb_column* outColumn,           // the column in the containing row
+    mdb_fill*   outBlobFill,         // the size of text content in bytes
+    mdbOid*     outChildOid,         // oid of possible row or table child
+    mdb_bool*   outIsRowChild) = 0;  // nonzero if child, and a row child
+
+  // Checking all cell metainfo is a good way to avoid forcing a large cell
+  // in to memory when you don't actually want to use the content.
+  
+  NS_IMETHOD GetRow(nsIMdbEnv* ev, // parent row for this cell
+    nsIMdbRow** acqRow) = 0;
+  NS_IMETHOD GetPort(nsIMdbEnv* ev, // port containing cell
+    nsIMdbPort** acqPort) = 0;
+  // } ----- end attribute methods -----
+
+  // { ----- begin children methods -----
+  NS_IMETHOD HasAnyChild( // does cell have a child instead of text?
+    nsIMdbEnv* ev,
+    mdbOid* outOid,  // out id of row or table (or unbound if no child)
+    mdb_bool* outIsRow) = 0; // nonzero if child is a row (rather than a table)
+
+  NS_IMETHOD GetAnyChild( // access table of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbRow** acqRow, // child row (or null)
+    nsIMdbTable** acqTable) = 0; // child table (or null)
+
+
+  NS_IMETHOD SetChildRow( // access table of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow) = 0; // inRow must be bound inside this same db port
+
+  NS_IMETHOD GetChildRow( // access row of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbRow** acqRow) = 0; // acquire child row (or nil if no child)
+
+
+  NS_IMETHOD SetChildTable( // access table of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbTable* inTable) = 0; // table must be bound inside this same db port
+
+  NS_IMETHOD GetChildTable( // access table of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbTable** acqTable) = 0; // acquire child table (or nil if no child)
+  // } ----- end children methods -----
+
+// } ===== end nsIMdbCell methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbCell, NS_IMDBTABLEROWCURSOR_IID)
+
+// } %%%%% end C++ abstract class interfaces %%%%%
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MDB_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/Makefile.in
@@ -0,0 +1,100 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= mork
+LIBRARY_NAME	= msgmork_s
+FORCE_STATIC_LIB=1
+LIBXUL_LIBRARY	= 1
+
+CPPSRCS		= \
+		orkinHeap.cpp \
+		morkArray.cpp \
+		morkAtom.cpp \
+		morkAtomMap.cpp \
+		morkAtomSpace.cpp \
+		morkBlob.cpp \
+		morkBuilder.cpp \
+		morkCell.cpp \
+		morkCellObject.cpp \
+		morkCh.cpp \
+		morkConfig.cpp \
+		morkCursor.cpp \
+		morkDeque.cpp \
+		morkEnv.cpp \
+		morkFactory.cpp \
+		morkFile.cpp \
+		morkHandle.cpp \
+		morkIntMap.cpp \
+		morkMap.cpp \
+		morkNode.cpp \
+		morkNodeMap.cpp \
+		morkObject.cpp \
+		morkParser.cpp \
+		morkPool.cpp \
+		morkRow.cpp \
+		morkRowCellCursor.cpp \
+		morkRowMap.cpp \
+		morkRowObject.cpp \
+		morkRowSpace.cpp \
+		morkSink.cpp \
+		morkSpace.cpp \
+		morkStore.cpp \
+		morkStream.cpp \
+		morkTable.cpp \
+		morkPortTableCursor.cpp \
+		morkTableRowCursor.cpp \
+		morkThumb.cpp \
+		morkWriter.cpp \
+		morkYarn.cpp \
+		morkBead.cpp \
+		morkProbeMap.cpp \
+		morkZone.cpp \
+		$(NULL)
+
+ifeq ($(OS_ARCH),WINNT)
+CPPSRCS		+= morkSearchRowCursor.cpp
+endif
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/mork.h
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORK_
+#define _MORK_ 1
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#include "nscore.h"
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+// { %%%%% begin disable unused param warnings %%%%%
+#define MORK_USED_1(x) (void)(&x)
+#define MORK_USED_2(x,y) (void)(&x);(void)(&y);
+#define MORK_USED_3(x,y,z) (void)(&x);(void)(&y);(void)(&z);
+#define MORK_USED_4(w,x,y,z) (void)(&w);(void)(&x);(void)(&y);(void)(&z);
+
+// } %%%%% end disable unused param warnings %%%%%
+
+// { %%%%% begin macro for finding class member offset %%%%%
+
+/*| OffsetOf: the unsigned integer offset of a class or struct
+**| field from the beginning of that class or struct.  This is
+**| the same as the similarly named public domain IronDoc macro,
+**| and is also the same as another macro appearing in stdlib.h.
+**| We want these offsets so we can correctly convert pointers
+**| to member slots back into pointers to enclosing objects, and
+**| have this exactly match what the compiler thinks is true.
+**|
+**|| Bascially we are asking the compiler to determine the offset at
+**| compile time, and we use the definition of address artithmetic
+**| to do this.  By casting integer zero to a pointer of type obj*,
+**| we can reference the address of a slot in such an object that
+**| is hypothetically physically placed at address zero, but without
+**| actually dereferencing a memory location.  The absolute address
+**| of slot is the same as offset of that slot, when the object is
+**| placed at address zero.
+|*/
+#define mork_OffsetOf(obj,slot) ((unsigned int)&((obj*) 0)->slot)
+
+// } %%%%% end macro for finding class member offset %%%%%
+
+// { %%%%% begin specific-size integer scalar typedefs %%%%%
+typedef unsigned char  mork_u1;  // make sure this is one byte
+typedef unsigned short mork_u2;  // make sure this is two bytes
+typedef short          mork_i2;  // make sure this is two bytes
+typedef PRUint32       mork_u4;  // make sure this is four bytes
+typedef PRInt32        mork_i4;  // make sure this is four bytes
+typedef PRWord         mork_ip;  // make sure sizeof(mork_ip) == sizeof(void*)
+
+typedef mork_u1 mork_ch;    // small byte-sized character (never wide)
+typedef mork_u1 mork_flags;  // one byte's worth of predicate bit flags
+
+typedef mork_u2 mork_base;    // 2-byte magic class signature slot in object
+typedef mork_u2 mork_derived; // 2-byte magic class signature slot in object
+typedef mork_u2 mork_uses;    // 2-byte strong uses count
+typedef mork_u2 mork_refs;    // 2-byte actual reference count
+
+typedef mork_u4 mork_token;      // unsigned token for atomized string
+typedef mork_token mork_scope;   // token used to id scope for rows
+typedef mork_token mork_kind;    // token used to id kind for tables
+typedef mork_token mork_cscode;  // token used to id charset names
+typedef mork_token mork_aid;     // token used to id atomize cell values
+
+typedef mork_token mork_column;  // token used to id columns for rows
+typedef mork_column mork_delta;  // mork_column plus mork_change 
+
+typedef mork_token mork_color;   // bead ID
+#define morkColor_kNone ((mork_color) 0)
+
+typedef mork_u4 mork_magic;      // unsigned magic signature
+
+typedef mork_u4 mork_seed;       // unsigned collection change counter
+typedef mork_u4 mork_count;      // unsigned collection member count
+typedef mork_count mork_num;     // synonym for count
+typedef mork_u4 mork_size;       // unsigned physical media size
+typedef mork_u4 mork_fill;       // unsigned logical content size
+typedef mork_u4 mork_more;       // more available bytes for larger buffer
+
+typedef mdb_u4 mork_percent; // 0..100, with values >100 same as 100
+
+typedef mork_i4 mork_pos; // negative means "before first" (at zero pos)
+typedef mork_i4 mork_line; // negative means "before first line in file"
+
+typedef mork_u1 mork_usage;   // 1-byte magic usage signature slot in object
+typedef mork_u1 mork_access;  // 1-byte magic access signature slot in object
+
+typedef mork_u1 mork_change; // add, cut, put, set, nil
+typedef mork_u1 mork_priority; // 0..9, for a total of ten different values
+
+typedef mork_u1 mork_able; // on, off, asleep (clone IronDoc's fe_able)
+typedef mork_u1 mork_load; // dirty or clean (clone IronDoc's fe_load)
+// } %%%%% end specific-size integer scalar typedefs %%%%%
+
+// 'test' is a public domain Mithril for key equality tests in probe maps
+typedef mork_i2 mork_test; /* neg=>kVoid, zero=>kHit, pos=>kMiss */
+
+#define morkTest_kVoid ((mork_test) -1) /* -1: nil key slot, no key order */
+#define morkTest_kHit  ((mork_test) 0)  /*  0: keys are equal, a map hit */
+#define morkTest_kMiss ((mork_test) 1)  /*  1: keys not equal, a map miss */
+
+// { %%%%% begin constants for Mork scalar types %%%%%
+#define morkPriority_kHi  ((mork_priority) 0) /* best priority */
+#define morkPriority_kMin ((mork_priority) 0) /* best priority is smallest */
+
+#define morkPriority_kLo  ((mork_priority) 9) /* worst priority */
+#define morkPriority_kMax ((mork_priority) 9) /* worst priority is biggest */
+
+#define morkPriority_kCount 10 /* number of distinct priority values */
+
+#define morkAble_kEnabled  ((mork_able) 0x55) /* same as IronDoc constant */
+#define morkAble_kDisabled ((mork_able) 0xAA) /* same as IronDoc constant */
+#define morkAble_kAsleep   ((mork_able) 0x5A) /* same as IronDoc constant */
+
+#define morkChange_kAdd 'a' /* add member */
+#define morkChange_kCut 'c' /* cut member */
+#define morkChange_kPut 'p' /* put member */
+#define morkChange_kSet 's' /* set all members */
+#define morkChange_kNil 0   /* no change in this member */
+#define morkChange_kDup 'd' /* duplicate changes have no effect */
+// kDup is intended to replace another change constant in an object as a
+// conclusion about change feasibility while staging intended alterations.
+
+#define morkLoad_kDirty ((mork_load) 0xDD) /* same as IronDoc constant */
+#define morkLoad_kClean ((mork_load) 0x22) /* same as IronDoc constant */
+
+#define morkAccess_kOpen    'o'
+#define morkAccess_kClosing 'c'
+#define morkAccess_kShut    's'
+#define morkAccess_kDead    'd'
+// } %%%%% end constants for Mork scalar types %%%%%
+
+// { %%%%% begin non-specific-size integer scalar typedefs %%%%%
+typedef int mork_char; // nominal type for ints used to hold input byte
+#define morkChar_IsWhite(c) \
+  ((c) == 0xA || (c) == 0x9 || (c) == 0xD || (c) == ' ')
+// } %%%%% end non-specific-size integer scalar typedefs %%%%%
+
+// { %%%%% begin mdb-driven scalar typedefs %%%%%
+// easier to define bool exactly the same as mdb:
+typedef mdb_bool mork_bool; // unsigned byte with zero=false, nonzero=true
+
+/* canonical boolean constants provided only for code clarity: */
+#define morkBool_kTrue  ((mork_bool) 1) /* actually any nonzero means true */
+#define morkBool_kFalse ((mork_bool) 0) /* only zero means false */
+
+// mdb clients can assign these, so we cannot pick maximum size:
+typedef mdb_id mork_id;    // unsigned object identity in a scope
+typedef mork_id mork_rid;  // unsigned row identity inside scope
+typedef mork_id mork_tid;  // unsigned table identity inside scope
+typedef mork_id mork_gid;  // unsigned group identity without any scope
+
+// we only care about neg, zero, pos -- so we don't care about size:
+typedef mdb_order mork_order; // neg:lessthan, zero:equalto, pos:greaterthan 
+// } %%%%% end mdb-driven scalar typedefs %%%%%
+
+#define morkId_kMinusOne ((mdb_id) -1)
+
+// { %%%%% begin class forward defines %%%%%
+// try to put these in alphabetical order for easier examination:
+class morkMid;
+class morkAtom;
+class morkAtomSpace;
+class morkBookAtom;
+class morkBuf;
+class morkBuilder;
+class morkCell;
+class morkCellObject;
+class morkCursor;
+class morkEnv;
+class morkFactory;
+class morkFile;
+class morkHandle;
+class morkHandleFace; // just an opaque cookie type
+class morkHandleFrame;
+class morkHashArrays;
+class morkMap;
+class morkNode;
+class morkObject;
+class morkOidAtom;
+class morkParser;
+class morkPool;
+class morkPlace;
+class morkPort;
+class morkPortTableCursor;
+class morkProbeMap;
+class morkRow;
+class morkRowCellCursor;
+class morkRowObject;
+class morkRowSpace;
+class morkSorting;
+class morkSortingRowCursor;
+class morkSpace;
+class morkSpan;
+class morkStore;
+class morkStream;
+class morkTable;
+class morkTableChange;
+class morkTableRowCursor;
+class morkThumb;
+class morkWriter;
+class morkZone;
+// } %%%%% end class forward defines %%%%%
+
+// include this config file last for platform & environment specific stuff:
+#ifndef _MORKCONFIG_
+#include "morkConfig.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORK_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkArray.cpp
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nscore.h"
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkArray.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkArray::CloseMorkNode(morkEnv* ev) // CloseTable() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseArray(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkArray::~morkArray() // assert CloseTable() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+  MORK_ASSERT(mArray_Slots==0);
+}
+
+/*public non-poly*/
+morkArray::morkArray(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, mork_size inSize, nsIMdbHeap* ioSlotHeap)
+: morkNode(ev, inUsage, ioHeap)
+, mArray_Slots( 0 )
+, mArray_Heap( 0 )
+, mArray_Fill( 0 )
+, mArray_Size( 0 )
+, mArray_Seed( (mork_u4)NS_PTR_TO_INT32(this) ) // "random" integer assignment
+{
+  if ( ev->Good() )
+  {
+    if ( ioSlotHeap )
+    {
+      nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mArray_Heap);
+      if ( ev->Good() )
+      {
+        if ( inSize < 3 )
+          inSize = 3;
+        mdb_size byteSize = inSize * sizeof(void*);
+        void** block = 0;
+        ioSlotHeap->Alloc(ev->AsMdbEnv(), byteSize, (void**) &block);
+        if ( block && ev->Good() )
+        {
+          mArray_Slots = block;
+          mArray_Size = inSize;
+          MORK_MEMSET(mArray_Slots, 0, byteSize);
+          if ( ev->Good() )
+            mNode_Derived = morkDerived_kArray;
+        }
+      }
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+/*public non-poly*/ void
+morkArray::CloseArray(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      if ( mArray_Heap && mArray_Slots )
+        mArray_Heap->Free(ev->AsMdbEnv(), mArray_Slots);
+        
+      mArray_Slots = 0;
+      mArray_Size = 0;
+      mArray_Fill = 0;
+      ++mArray_Seed;
+      nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mArray_Heap);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkArray::NonArrayTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkArray");
+}
+
+/*static*/ void
+morkArray::IndexBeyondEndError(morkEnv* ev)
+{
+  ev->NewError("array index beyond end");
+}
+
+/*static*/ void
+morkArray::NilSlotsAddressError(morkEnv* ev)
+{
+  ev->NewError("nil mArray_Slots");
+}
+
+/*static*/ void
+morkArray::FillBeyondSizeError(morkEnv* ev)
+{
+  ev->NewError("mArray_Fill > mArray_Size");
+}
+
+mork_bool
+morkArray::Grow(morkEnv* ev, mork_size inNewSize)
+// Grow() returns true if capacity becomes >= inNewSize and ev->Good()
+{
+  if ( ev->Good() && inNewSize > mArray_Size ) // make array larger?
+  {
+    if ( mArray_Fill <= mArray_Size ) // fill and size fit the invariant?
+    {
+      if (mArray_Size <= 3)
+        inNewSize = mArray_Size + 3;
+      else
+        inNewSize = mArray_Size  * 2;// + 3;  // try doubling size here - used to grow by 3
+        
+      mdb_size newByteSize = inNewSize * sizeof(void*);
+      void** newBlock = 0;
+      mArray_Heap->Alloc(ev->AsMdbEnv(), newByteSize, (void**) &newBlock);
+      if ( newBlock && ev->Good() ) // okay new block?
+      {
+        void** oldSlots = mArray_Slots;
+        void** oldEnd = oldSlots + mArray_Fill;
+        
+        void** newSlots = newBlock;
+        void** newEnd = newBlock + inNewSize;
+        
+        while ( oldSlots < oldEnd )
+          *newSlots++ = *oldSlots++;
+          
+        while ( newSlots < newEnd )
+          *newSlots++ = (void*) 0;
+
+        oldSlots = mArray_Slots;
+        mArray_Size = inNewSize;
+        mArray_Slots = newBlock;
+        mArray_Heap->Free(ev->AsMdbEnv(), oldSlots);
+      }
+    }
+    else
+      this->FillBeyondSizeError(ev);
+  }
+  ++mArray_Seed; // always modify seed, since caller intends to add slots
+  return ( ev->Good() && mArray_Size >= inNewSize );
+}
+
+void*
+morkArray::SafeAt(morkEnv* ev, mork_pos inPos)
+{
+  if ( mArray_Slots )
+  {
+    if ( inPos >= 0 && inPos < (mork_pos) mArray_Fill )
+      return mArray_Slots[ inPos ];
+    else
+      this->IndexBeyondEndError(ev);
+  }
+  else
+    this->NilSlotsAddressError(ev);
+    
+  return (void*) 0;
+}
+
+void
+morkArray::SafeAtPut(morkEnv* ev, mork_pos inPos, void* ioSlot)
+{
+  if ( mArray_Slots )
+  {
+    if ( inPos >= 0 && inPos < (mork_pos) mArray_Fill )
+    {
+      mArray_Slots[ inPos ] = ioSlot;
+      ++mArray_Seed;
+    }
+    else
+      this->IndexBeyondEndError(ev);
+  }
+  else
+    this->NilSlotsAddressError(ev);
+}
+
+mork_pos
+morkArray::AppendSlot(morkEnv* ev, void* ioSlot)
+{
+  mork_pos outPos = -1;
+  if ( mArray_Slots )
+  {
+    mork_fill fill = mArray_Fill;
+    if ( this->Grow(ev, fill+1) )
+    {
+      outPos = (mork_pos) fill;
+      mArray_Slots[ fill ] = ioSlot;
+      mArray_Fill = fill + 1;
+      // note Grow() increments mArray_Seed
+    }
+  }
+  else
+    this->NilSlotsAddressError(ev);
+    
+  return outPos;
+}
+
+void
+morkArray::AddSlot(morkEnv* ev, mork_pos inPos, void* ioSlot)
+{
+  if ( mArray_Slots )
+  {
+    mork_fill fill = mArray_Fill;
+    if ( this->Grow(ev, fill+1) )
+    {
+      void** slot = mArray_Slots; // the slot vector
+      void** end = slot + fill; // one past the last used array slot
+      slot += inPos; // the slot to be added
+
+      while ( --end >= slot ) // another slot to move upward?
+        end[ 1 ] = *end;
+
+      *slot = ioSlot;
+      mArray_Fill = fill + 1;
+      // note Grow() increments mArray_Seed
+    }
+  }
+  else
+    this->NilSlotsAddressError(ev);
+}
+
+void
+morkArray::CutSlot(morkEnv* ev, mork_pos inPos)
+{
+  MORK_USED_1(ev);
+  mork_fill fill = mArray_Fill;
+  if ( inPos >= 0 && inPos < (mork_pos) fill ) // cutting slot in used array portion?
+  {
+    void** slot = mArray_Slots; // the slot vector
+    void** end = slot + fill; // one past the last used array slot
+    slot += inPos; // the slot to be cut
+    
+    while ( ++slot < end ) // another slot to move downward?
+      slot[ -1 ] = *slot;
+      
+    slot[ -1 ] = 0; // clear the last used slot which is now unused
+    
+    // note inPos<fill implies fill>0, so fill-1 must be nonnegative:
+    mArray_Fill = fill - 1;
+    ++mArray_Seed;
+  }
+}
+
+void
+morkArray::CutAllSlots(morkEnv* ev)
+{
+  if ( mArray_Slots )
+  {
+    if ( mArray_Fill <= mArray_Size )
+    {
+      mdb_size oldByteSize = mArray_Fill * sizeof(void*);
+      MORK_MEMSET(mArray_Slots, 0, oldByteSize);
+    }
+    else
+      this->FillBeyondSizeError(ev);
+  }
+  else
+    this->NilSlotsAddressError(ev);
+
+  ++mArray_Seed;
+  mArray_Fill = 0;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkArray.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKARRAY_
+#define _MORKARRAY_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kArray  /*i*/ 0x4179 /* ascii 'Ay' */
+
+class morkArray : public morkNode { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+  void**       mArray_Slots; // array of pointers
+  nsIMdbHeap*  mArray_Heap;  // required heap for allocating mArray_Slots
+  mork_fill    mArray_Fill;  // logical count of used slots in mArray_Slots
+  mork_size    mArray_Size;  // physical count of mArray_Slots ( >= Fill)
+  mork_seed    mArray_Seed;  // change counter for syncing with iterators
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseArray()
+  virtual ~morkArray(); // assert that close executed earlier
+  
+public: // morkArray construction & destruction
+  morkArray(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, mork_size inSize, nsIMdbHeap* ioSlotHeap);
+  void CloseArray(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkArray(const morkArray& other);
+  morkArray& operator=(const morkArray& other);
+
+public: // dynamic type identification
+  mork_bool IsArray() const
+  { return IsNode() && mNode_Derived == morkDerived_kArray; }
+// } ===== end morkNode methods =====
+
+public: // typing & errors
+  static void NonArrayTypeError(morkEnv* ev);
+  static void IndexBeyondEndError(morkEnv* ev);
+  static void NilSlotsAddressError(morkEnv* ev);
+  static void FillBeyondSizeError(morkEnv* ev);
+
+public: // other table row cursor methods
+
+  mork_fill  Length() const { return mArray_Fill; }
+  mork_size  Capacity() const { return mArray_Size; }
+  
+  mork_bool  Grow(morkEnv* ev, mork_size inNewSize);
+  // Grow() returns true if capacity becomes >= inNewSize and ev->Good()
+  
+  void*      At(mork_pos inPos) const { return mArray_Slots[ inPos ]; }
+  void       AtPut(mork_pos inPos, void* ioSlot)
+  { mArray_Slots[ inPos ] = ioSlot; }
+  
+  void*      SafeAt(morkEnv* ev, mork_pos inPos);
+  void       SafeAtPut(morkEnv* ev, mork_pos inPos, void* ioSlot);
+  
+  mork_pos   AppendSlot(morkEnv* ev, void* ioSlot);
+  void       AddSlot(morkEnv* ev, mork_pos inPos, void* ioSlot);
+  void       CutSlot(morkEnv* ev, mork_pos inPos);
+  void       CutAllSlots(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakArray(morkArray* me,
+    morkEnv* ev, morkArray** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongArray(morkArray* me,
+    morkEnv* ev, morkArray** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTABLEROWCURSOR_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkAtom.cpp
@@ -0,0 +1,604 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+mork_bool
+morkAtom::GetYarn(mdbYarn* outYarn) const
+{
+  const void* source = 0;  
+  mdb_fill fill = 0; 
+  mdb_cscode form = 0;
+  outYarn->mYarn_More = 0;
+
+  if ( this )
+  {
+    if ( this->IsWeeBook() )
+    {
+      morkWeeBookAtom* weeBook = (morkWeeBookAtom*) this;
+      source = weeBook->mWeeBookAtom_Body;
+      fill = weeBook->mAtom_Size;
+    }
+    else if ( this->IsBigBook() )
+    {
+      morkBigBookAtom* bigBook = (morkBigBookAtom*) this;
+      source = bigBook->mBigBookAtom_Body;
+      fill = bigBook->mBigBookAtom_Size;
+      form = bigBook->mBigBookAtom_Form;
+    }
+    else if ( this->IsWeeAnon() )
+    {
+      morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*) this;
+      source = weeAnon->mWeeAnonAtom_Body;
+      fill = weeAnon->mAtom_Size;
+    }
+    else if ( this->IsBigAnon() )
+    {
+      morkBigAnonAtom* bigAnon = (morkBigAnonAtom*) this;
+      source = bigAnon->mBigAnonAtom_Body;
+      fill = bigAnon->mBigAnonAtom_Size;
+      form = bigAnon->mBigAnonAtom_Form;
+    }
+  }
+  if ( source && fill ) // have an atom with nonempty content?
+  {
+    // if we have too many bytes, and yarn seems growable:
+    if ( fill > outYarn->mYarn_Size && outYarn->mYarn_Grow ) // try grow?
+      (*outYarn->mYarn_Grow)(outYarn, (mdb_size) fill); // request bigger
+      
+    mdb_size size = outYarn->mYarn_Size; // max dest size
+    if ( fill > size ) // too much atom content?
+    {
+      outYarn->mYarn_More = fill - size; // extra atom bytes omitted
+      fill = size; // copy no more bytes than size of yarn buffer
+    }
+    void* dest = outYarn->mYarn_Buf; // where bytes are going
+    if ( !dest ) // nil destination address buffer?
+      fill = 0; // we can't write any content at all
+      
+    if ( fill ) // anything to copy?
+      MORK_MEMCPY(dest, source, fill); // copy fill bytes to yarn
+      
+    outYarn->mYarn_Fill = fill; // tell yarn size of copied content
+  }
+  else // no content to put into the yarn
+  {
+    outYarn->mYarn_Fill = 0; // tell yarn that atom has no bytes
+  }
+  outYarn->mYarn_Form = form; // always update the form slot
+  
+  return ( source != 0 );
+}
+
+mork_bool
+morkAtom::AsBuf(morkBuf& outBuf) const
+{
+  const morkAtom* atom = this;
+  if ( atom )
+  {
+    if ( atom->IsWeeBook() )
+    {
+      morkWeeBookAtom* weeBook = (morkWeeBookAtom*) atom;
+      outBuf.mBuf_Body = weeBook->mWeeBookAtom_Body;
+      outBuf.mBuf_Fill = weeBook->mAtom_Size;
+    }
+    else if ( atom->IsBigBook() )
+    {
+      morkBigBookAtom* bigBook = (morkBigBookAtom*) atom;
+      outBuf.mBuf_Body = bigBook->mBigBookAtom_Body;
+      outBuf.mBuf_Fill = bigBook->mBigBookAtom_Size;
+    }
+    else if ( atom->IsWeeAnon() )
+    {
+      morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*) atom;
+      outBuf.mBuf_Body = weeAnon->mWeeAnonAtom_Body;
+      outBuf.mBuf_Fill = weeAnon->mAtom_Size;
+    }
+    else if ( atom->IsBigAnon() )
+    {
+      morkBigAnonAtom* bigAnon = (morkBigAnonAtom*) atom;
+      outBuf.mBuf_Body = bigAnon->mBigAnonAtom_Body;
+      outBuf.mBuf_Fill = bigAnon->mBigAnonAtom_Size;
+    }
+    else
+      atom = 0; // show desire to put empty content in yarn
+  }
+  
+  if ( !atom ) // empty content for yarn?
+  {
+    outBuf.mBuf_Body = 0;
+    outBuf.mBuf_Fill = 0;
+  }
+  return ( atom != 0 );
+}
+
+mork_bool
+morkAtom::AliasYarn(mdbYarn* outYarn) const
+{
+  outYarn->mYarn_More = 0;
+  outYarn->mYarn_Form = 0;
+  const morkAtom* atom = this;
+  
+  if ( atom )
+  {
+    if ( atom->IsWeeBook() )
+    {
+      morkWeeBookAtom* weeBook = (morkWeeBookAtom*) atom;
+      outYarn->mYarn_Buf = weeBook->mWeeBookAtom_Body;
+      outYarn->mYarn_Fill = weeBook->mAtom_Size;
+      outYarn->mYarn_Size = weeBook->mAtom_Size;
+    }
+    else if ( atom->IsBigBook() )
+    {
+      morkBigBookAtom* bigBook = (morkBigBookAtom*) atom;
+      outYarn->mYarn_Buf = bigBook->mBigBookAtom_Body;
+      outYarn->mYarn_Fill = bigBook->mBigBookAtom_Size;
+      outYarn->mYarn_Size = bigBook->mBigBookAtom_Size;
+      outYarn->mYarn_Form = bigBook->mBigBookAtom_Form;
+    }
+    else if ( atom->IsWeeAnon() )
+    {
+      morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*) atom;
+      outYarn->mYarn_Buf = weeAnon->mWeeAnonAtom_Body;
+      outYarn->mYarn_Fill = weeAnon->mAtom_Size;
+      outYarn->mYarn_Size = weeAnon->mAtom_Size;
+    }
+    else if ( atom->IsBigAnon() )
+    {
+      morkBigAnonAtom* bigAnon = (morkBigAnonAtom*) atom;
+      outYarn->mYarn_Buf = bigAnon->mBigAnonAtom_Body;
+      outYarn->mYarn_Fill = bigAnon->mBigAnonAtom_Size;
+      outYarn->mYarn_Size = bigAnon->mBigAnonAtom_Size;
+      outYarn->mYarn_Form = bigAnon->mBigAnonAtom_Form;
+    }
+    else
+      atom = 0; // show desire to put empty content in yarn
+  }
+  
+  if ( !atom ) // empty content for yarn?
+  {
+    outYarn->mYarn_Buf = 0;
+    outYarn->mYarn_Fill = 0;
+    outYarn->mYarn_Size = 0;
+    // outYarn->mYarn_Grow = 0; // please don't modify the Grow slot
+  }
+  return ( atom != 0 );
+}
+
+mork_aid
+morkAtom::GetBookAtomAid() const // zero or book atom's ID
+{
+  return ( this->IsBook() )? ((morkBookAtom*) this)->mBookAtom_Id : 0;
+}
+
+mork_scope
+morkAtom::GetBookAtomSpaceScope(morkEnv* ev) const // zero or book's space's scope
+{
+  mork_scope outScope = 0;
+  if ( this->IsBook() )
+  {
+    const morkBookAtom* bookAtom = (const morkBookAtom*) this;
+    morkAtomSpace* space = bookAtom->mBookAtom_Space;
+    if ( space->IsAtomSpace() )
+      outScope = space->SpaceScope();
+    else
+      space->NonAtomSpaceTypeError(ev);
+  }
+  
+  return outScope;
+}
+
+void
+morkAtom::MakeCellUseForever(morkEnv* ev)
+{
+  MORK_USED_1(ev); 
+  mAtom_CellUses = morkAtom_kForeverCellUses;
+}
+
+mork_u1
+morkAtom::AddCellUse(morkEnv* ev)
+{
+  MORK_USED_1(ev); 
+  if ( mAtom_CellUses < morkAtom_kMaxCellUses ) // not already maxed out?
+    ++mAtom_CellUses;
+    
+  return mAtom_CellUses;
+}
+
+mork_u1
+morkAtom::CutCellUse(morkEnv* ev)
+{
+  if ( mAtom_CellUses ) // any outstanding uses to cut?
+  {
+    if ( mAtom_CellUses < morkAtom_kMaxCellUses ) // not frozen at max?
+      --mAtom_CellUses;
+  }
+  else
+    this->CellUsesUnderflowWarning(ev);
+    
+  return mAtom_CellUses;
+}
+
+/*static*/ void
+morkAtom::CellUsesUnderflowWarning(morkEnv* ev)
+{
+  ev->NewWarning("mAtom_CellUses underflow");
+}
+
+/*static*/ void
+morkAtom::BadAtomKindError(morkEnv* ev)
+{
+  ev->NewError("bad mAtom_Kind");
+}
+
+/*static*/ void
+morkAtom::ZeroAidError(morkEnv* ev)
+{
+  ev->NewError("zero atom ID");
+}
+
+/*static*/ void
+morkAtom::AtomSizeOverflowError(morkEnv* ev)
+{
+  ev->NewError("atom mAtom_Size overflow");
+}
+
+void
+morkOidAtom::InitRowOidAtom(morkEnv* ev, const mdbOid& inOid)
+{
+  MORK_USED_1(ev); 
+  mAtom_CellUses = 0;
+  mAtom_Kind = morkAtom_kKindRowOid;
+  mAtom_Change = morkChange_kNil;
+  mAtom_Size = 0;
+  mOidAtom_Oid = inOid; // bitwise copy
+}
+
+void
+morkOidAtom::InitTableOidAtom(morkEnv* ev, const mdbOid& inOid)
+{
+  MORK_USED_1(ev); 
+  mAtom_CellUses = 0;
+  mAtom_Kind = morkAtom_kKindTableOid;
+  mAtom_Change = morkChange_kNil;
+  mAtom_Size = 0;
+  mOidAtom_Oid = inOid; // bitwise copy
+}
+
+void
+morkWeeAnonAtom::InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf)
+{
+  mAtom_Kind = 0;
+  mAtom_Change = morkChange_kNil;
+  if ( inBuf.mBuf_Fill <= morkAtom_kMaxByteSize )
+  {
+    mAtom_CellUses = 0;
+    mAtom_Kind = morkAtom_kKindWeeAnon;
+    mork_size size = inBuf.mBuf_Fill;
+    mAtom_Size = (mork_u1) size;
+    if ( size && inBuf.mBuf_Body )
+      MORK_MEMCPY(mWeeAnonAtom_Body, inBuf.mBuf_Body, size);
+        
+    mWeeAnonAtom_Body[ size ] = 0;
+  }
+  else
+    this->AtomSizeOverflowError(ev);
+}
+
+void
+morkBigAnonAtom::InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf,
+  mork_cscode inForm)
+{
+  MORK_USED_1(ev); 
+  mAtom_CellUses = 0;
+  mAtom_Kind = morkAtom_kKindBigAnon;
+  mAtom_Change = morkChange_kNil;
+  mAtom_Size = 0;
+  mBigAnonAtom_Form = inForm;
+  mork_size size = inBuf.mBuf_Fill;
+  mBigAnonAtom_Size = size;
+  if ( size && inBuf.mBuf_Body )
+    MORK_MEMCPY(mBigAnonAtom_Body, inBuf.mBuf_Body, size);
+        
+  mBigAnonAtom_Body[ size ] = 0;
+}
+
+/*static*/ void
+morkBookAtom::NonBookAtomTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkBookAtom");
+}
+
+mork_u4
+morkBookAtom::HashFormAndBody(morkEnv* ev) const
+{
+  // This hash is obviously a variation of the dragon book string hash.
+  // (I won't bother to explain or rationalize this usage for you.)
+  
+  register mork_u4 outHash = 0; // hash value returned
+  register unsigned char c; // next character
+  register const mork_u1* body; // body of bytes to hash
+  mork_size size = 0; // the number of bytes to hash
+
+  if ( this->IsWeeBook() )
+  {
+    size = mAtom_Size;
+    body = ((const morkWeeBookAtom*) this)->mWeeBookAtom_Body;
+  }
+  else if ( this->IsBigBook() )
+  {
+    size = ((const morkBigBookAtom*) this)->mBigBookAtom_Size;
+    body = ((const morkBigBookAtom*) this)->mBigBookAtom_Body;
+  }
+  else if ( this->IsFarBook() )
+  {
+    size = ((const morkFarBookAtom*) this)->mFarBookAtom_Size;
+    body = ((const morkFarBookAtom*) this)->mFarBookAtom_Body;
+  }
+  else
+  {
+    this->NonBookAtomTypeError(ev);
+    return 0;
+  }
+  
+  const mork_u1* end = body + size;
+  while ( body < end )
+  {
+    c = *body++;
+    outHash <<= 4;
+    outHash += c;
+    mork_u4 top = outHash & 0xF0000000L; // top four bits
+    if ( top ) // any of high four bits equal to one? 
+    {
+      outHash ^= (top >> 24); // fold down high bits
+      outHash ^= top; // zero top four bits
+    }
+  }
+    
+  return outHash;
+}
+
+mork_bool
+morkBookAtom::EqualFormAndBody(morkEnv* ev, const morkBookAtom* inAtom) const
+{
+  mork_bool outEqual = morkBool_kFalse;
+  
+  const mork_u1* body = 0; // body of inAtom bytes to compare
+  mork_size size; // the number of inAtom bytes to compare
+  mork_cscode form; // nominal charset for ioAtom
+
+  if ( inAtom->IsWeeBook() )
+  {
+    size = inAtom->mAtom_Size;
+    body = ((const morkWeeBookAtom*) inAtom)->mWeeBookAtom_Body;
+    form = 0;
+  }
+  else if ( inAtom->IsBigBook() )
+  {
+    size = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Size;
+    body = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Body;
+    form = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Form;
+  }
+  else if ( inAtom->IsFarBook() )
+  {
+    size = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Size;
+    body = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Body;
+    form = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Form;
+  }
+  else
+  {
+    inAtom->NonBookAtomTypeError(ev);
+    return morkBool_kFalse;
+  }
+
+  const mork_u1* thisBody = 0; // body of bytes in this to compare
+  mork_size thisSize; // the number of bytes in this to compare
+  mork_cscode thisForm; // nominal charset for this atom
+  
+  if ( this->IsWeeBook() )
+  {
+    thisSize = mAtom_Size;
+    thisBody = ((const morkWeeBookAtom*) this)->mWeeBookAtom_Body;
+    thisForm = 0;
+  }
+  else if ( this->IsBigBook() )
+  {
+    thisSize = ((const morkBigBookAtom*) this)->mBigBookAtom_Size;
+    thisBody = ((const morkBigBookAtom*) this)->mBigBookAtom_Body;
+    thisForm = ((const morkBigBookAtom*) this)->mBigBookAtom_Form;
+  }
+  else if ( this->IsFarBook() )
+  {
+    thisSize = ((const morkFarBookAtom*) this)->mFarBookAtom_Size;
+    thisBody = ((const morkFarBookAtom*) this)->mFarBookAtom_Body;
+    thisForm = ((const morkFarBookAtom*) this)->mFarBookAtom_Form;
+  }
+  else
+  {
+    this->NonBookAtomTypeError(ev);
+    return morkBool_kFalse;
+  }
+  
+  // if atoms are empty, form is irrelevant
+  if ( body && thisBody && size == thisSize && (!size || form == thisForm ))
+    outEqual = (MORK_MEMCMP(body, thisBody, size) == 0);
+  
+  return outEqual;
+}
+
+
+void
+morkBookAtom::CutBookAtomFromSpace(morkEnv* ev)
+{
+  morkAtomSpace* space = mBookAtom_Space;
+  if ( space )
+  {
+    mBookAtom_Space = 0;
+    space->mAtomSpace_AtomBodies.CutAtom(ev, this);
+    space->mAtomSpace_AtomAids.CutAtom(ev, this);
+  }
+  else
+    ev->NilPointerError();
+}
+
+morkWeeBookAtom::morkWeeBookAtom(mork_aid inAid)
+{
+  mAtom_Kind = morkAtom_kKindWeeBook;
+  mAtom_CellUses = 0;
+  mAtom_Change = morkChange_kNil;
+  mAtom_Size = 0;
+
+  mBookAtom_Space = 0;
+  mBookAtom_Id = inAid;
+
+  mWeeBookAtom_Body[ 0 ] = 0;
+}
+
+void
+morkWeeBookAtom::InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf,
+  morkAtomSpace* ioSpace, mork_aid inAid)
+{
+  mAtom_Kind = 0;
+  mAtom_Change = morkChange_kNil;
+  if ( ioSpace )
+  {
+    if ( inAid )
+    {
+      if ( inBuf.mBuf_Fill <= morkAtom_kMaxByteSize )
+      {
+        mAtom_CellUses = 0;
+        mAtom_Kind = morkAtom_kKindWeeBook;
+        mBookAtom_Space = ioSpace;
+        mBookAtom_Id = inAid;
+        mork_size size = inBuf.mBuf_Fill;
+        mAtom_Size = (mork_u1) size;
+        if ( size && inBuf.mBuf_Body )
+          MORK_MEMCPY(mWeeBookAtom_Body, inBuf.mBuf_Body, size);
+        
+        mWeeBookAtom_Body[ size ] = 0;
+      }
+      else
+        this->AtomSizeOverflowError(ev);
+    }
+    else
+      this->ZeroAidError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+void
+morkBigBookAtom::InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf,
+  mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid)
+{
+  mAtom_Kind = 0;
+  mAtom_Change = morkChange_kNil;
+  if ( ioSpace )
+  {
+    if ( inAid )
+    {
+      mAtom_CellUses = 0;
+      mAtom_Kind = morkAtom_kKindBigBook;
+      mAtom_Size = 0;
+      mBookAtom_Space = ioSpace;
+      mBookAtom_Id = inAid;
+      mBigBookAtom_Form = inForm;
+      mork_size size = inBuf.mBuf_Fill;
+      mBigBookAtom_Size = size;
+      if ( size && inBuf.mBuf_Body )
+        MORK_MEMCPY(mBigBookAtom_Body, inBuf.mBuf_Body, size);
+        
+      mBigBookAtom_Body[ size ] = 0;
+    }
+    else
+      this->ZeroAidError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+void morkFarBookAtom::InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf,
+  mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid)
+{
+  mAtom_Kind = 0;
+  mAtom_Change = morkChange_kNil;
+  if ( ioSpace )
+  {
+    if ( inAid )
+    {
+      mAtom_CellUses = 0;
+      mAtom_Kind = morkAtom_kKindFarBook;
+      mAtom_Size = 0;
+      mBookAtom_Space = ioSpace;
+      mBookAtom_Id = inAid;
+      mFarBookAtom_Form = inForm;
+      mFarBookAtom_Size = inBuf.mBuf_Fill;
+      mFarBookAtom_Body = (mork_u1*) inBuf.mBuf_Body;
+    }
+    else
+      this->ZeroAidError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkAtom.h
@@ -0,0 +1,398 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKATOM_
+#define _MORKATOM_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+#define morkAtom_kMaxByteSize 255 /* max for 8-bit integer */
+#define morkAtom_kForeverCellUses 0x0FF /* max for 8-bit integer */
+#define morkAtom_kMaxCellUses 0x07F /* max for 7-bit integer */
+
+#define morkAtom_kKindWeeAnon  'a'  /* means morkWeeAnonAtom subclass */
+#define morkAtom_kKindBigAnon  'A'  /* means morkBigAnonAtom subclass */
+#define morkAtom_kKindWeeBook  'b'  /* means morkWeeBookAtom subclass */
+#define morkAtom_kKindBigBook  'B'  /* means morkBigBookAtom subclass */
+#define morkAtom_kKindFarBook  'f'  /* means morkFarBookAtom subclass */
+#define morkAtom_kKindRowOid   'r'  /* means morkOidAtom subclass */
+#define morkAtom_kKindTableOid 't'  /* means morkOidAtom subclass */
+
+/*| Atom: .
+|*/
+class morkAtom { //
+ 
+public: 
+
+  mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  mork_change   mAtom_Change;    // how has this atom been changed?
+  mork_u1       mAtom_Size;      // only for atoms smaller than 256 bytes
+
+public: 
+  morkAtom(mork_aid inAid, mork_u1 inKind);
+  
+  mork_bool IsWeeAnon() const { return mAtom_Kind == morkAtom_kKindWeeAnon; }
+  mork_bool IsBigAnon() const { return mAtom_Kind == morkAtom_kKindBigAnon; }
+  mork_bool IsWeeBook() const { return mAtom_Kind == morkAtom_kKindWeeBook; }
+  mork_bool IsBigBook() const { return mAtom_Kind == morkAtom_kKindBigBook; }
+  mork_bool IsFarBook() const { return mAtom_Kind == morkAtom_kKindFarBook; }
+  mork_bool IsRowOid() const { return mAtom_Kind == morkAtom_kKindRowOid; }
+  mork_bool IsTableOid() const { return mAtom_Kind == morkAtom_kKindTableOid; }
+
+  mork_bool IsBook() const { return this->IsWeeBook() || this->IsBigBook(); }
+
+public: // clean vs dirty
+
+  void SetAtomClean() { mAtom_Change = morkChange_kNil; }
+  void SetAtomDirty() { mAtom_Change = morkChange_kAdd; }
+  
+  mork_bool IsAtomClean() const { return mAtom_Change == morkChange_kNil; }
+  mork_bool IsAtomDirty() const { return mAtom_Change == morkChange_kAdd; }
+
+public: // atom space scope if IsBook() is true, or else zero:
+
+  mork_scope GetBookAtomSpaceScope(morkEnv* ev) const;
+  // zero or book's space's scope
+
+  mork_aid   GetBookAtomAid() const;
+  // zero or book atom's ID
+ 
+public: // empty construction does nothing
+  morkAtom() { }
+
+public: // one-byte refcounting, freezing at maximum
+  void       MakeCellUseForever(morkEnv* ev);
+  mork_u1    AddCellUse(morkEnv* ev);
+  mork_u1    CutCellUse(morkEnv* ev);
+  
+  mork_bool  IsCellUseForever() const 
+  { return mAtom_CellUses == morkAtom_kForeverCellUses; }
+  
+private: // warnings
+
+  static void CellUsesUnderflowWarning(morkEnv* ev);
+
+public: // errors
+
+  static void BadAtomKindError(morkEnv* ev);
+  static void ZeroAidError(morkEnv* ev);
+  static void AtomSizeOverflowError(morkEnv* ev);
+
+public: // yarns
+  
+  mork_bool   AsBuf(morkBuf& outBuf) const;
+  mork_bool   AliasYarn(mdbYarn* outYarn) const;
+  mork_bool   GetYarn(mdbYarn* outYarn) const;
+
+private: // copying is not allowed
+  morkAtom(const morkAtom& other);
+  morkAtom& operator=(const morkAtom& other);
+};
+
+/*| OidAtom: an atom that references a row or table by identity.
+|*/
+class morkOidAtom : public morkAtom { //
+
+  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  // mork_change   mAtom_Change;    // how has this atom been changed?
+  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms
+ 
+public:
+  mdbOid           mOidAtom_Oid;       // identity of referenced object
+
+public: // empty construction does nothing
+  morkOidAtom() { }
+  void InitRowOidAtom(morkEnv* ev, const mdbOid& inOid);
+  void InitTableOidAtom(morkEnv* ev, const mdbOid& inOid);
+
+private: // copying is not allowed
+  morkOidAtom(const morkOidAtom& other);
+  morkOidAtom& operator=(const morkOidAtom& other);
+};
+
+/*| WeeAnonAtom: an atom whose content immediately follows morkAtom slots
+**| in an inline fashion, so that morkWeeAnonAtom contains both leading
+**| atom slots and then the content bytes without further overhead.  Note
+**| that charset encoding is not indicated, so zero is implied for Latin1.
+**| (Non-Latin1 content must be stored in a morkBigAnonAtom with a charset.)
+**|
+**|| An anon (anonymous) atom has no identity, with no associated bookkeeping
+**| for lookup needed for sharing like a book atom.
+**|
+**|| A wee anon atom is immediate but not shared with any other users of this
+**| atom, so no bookkeeping for sharing is needed.  This means the atom has
+**| no ID, because the atom has no identity other than this immediate content,
+**| and no hash table is needed to look up this particular atom.  This also
+**| applies to the larger format morkBigAnonAtom, which has more slots.
+|*/
+class morkWeeAnonAtom : public morkAtom { //
+
+  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  // mork_change   mAtom_Change;    // how has this atom been changed?
+  // mork_u1       mAtom_Size;      // only for atoms smaller than 256 bytes
+  
+public:
+  mork_u1 mWeeAnonAtom_Body[ 1 ]; // 1st byte of immediate content vector
+
+public: // empty construction does nothing
+  morkWeeAnonAtom() { }
+  void InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf);
+  
+  // allow extra trailing byte for a null byte:
+  static mork_size SizeForFill(mork_fill inFill)
+  { return sizeof(morkWeeAnonAtom) + inFill; }
+
+private: // copying is not allowed
+  morkWeeAnonAtom(const morkWeeAnonAtom& other);
+  morkWeeAnonAtom& operator=(const morkWeeAnonAtom& other);
+};
+
+/*| BigAnonAtom: another immediate atom that cannot be encoded as the smaller
+**| morkWeeAnonAtom format because either the size is too great, and/or the
+**| charset is not the default zero for Latin1 and must be explicitly noted.
+**|
+**|| An anon (anonymous) atom has no identity, with no associated bookkeeping
+**| for lookup needed for sharing like a book atom.
+|*/
+class morkBigAnonAtom : public morkAtom { //
+
+  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  // mork_change   mAtom_Change;    // how has this atom been changed?
+  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms
+ 
+public:
+  mork_cscode   mBigAnonAtom_Form;      // charset format encoding
+  mork_size     mBigAnonAtom_Size;      // size of content vector
+  mork_u1       mBigAnonAtom_Body[ 1 ]; // 1st byte of immed content vector
+
+public: // empty construction does nothing
+  morkBigAnonAtom() { }
+  void InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm);
+  
+  // allow extra trailing byte for a null byte:
+  static mork_size SizeForFill(mork_fill inFill)
+  { return sizeof(morkBigAnonAtom) + inFill; }
+
+private: // copying is not allowed
+  morkBigAnonAtom(const morkBigAnonAtom& other);
+  morkBigAnonAtom& operator=(const morkBigAnonAtom& other);
+};
+
+#define morkBookAtom_kMaxBodySize 1024 /* if larger, cannot be shared */
+
+/*| BookAtom: the common subportion of wee book atoms and big book atoms that
+**| includes the atom ID and the pointer to the space referencing this atom
+**| through a hash table.
+|*/
+class morkBookAtom : public morkAtom { //
+  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  // mork_change   mAtom_Change;    // how has this atom been changed?
+  // mork_u1       mAtom_Size;      // only for atoms smaller than 256 bytes
+  
+public:
+  morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is atom scope 
+  mork_aid       mBookAtom_Id;    // identity token for this shared atom
+
+public: // empty construction does nothing
+  morkBookAtom() { }
+
+  static void NonBookAtomTypeError(morkEnv* ev);
+
+public: // Hash() and Equal() for atom ID maps are same for all subclasses:
+
+  mork_u4 HashAid() const { return mBookAtom_Id; }
+  mork_bool EqualAid(const morkBookAtom* inAtom) const
+  { return ( mBookAtom_Id == inAtom->mBookAtom_Id); }
+
+public: // Hash() and Equal() for atom body maps know about subclasses:
+  
+  // YOU CANNOT SUBCLASS morkBookAtom WITHOUT FIXING Hash and Equal METHODS:
+
+  mork_u4 HashFormAndBody(morkEnv* ev) const;
+  mork_bool EqualFormAndBody(morkEnv* ev, const morkBookAtom* inAtom) const;
+  
+public: // separation from containing space
+
+  void CutBookAtomFromSpace(morkEnv* ev);
+
+private: // copying is not allowed
+  morkBookAtom(const morkBookAtom& other);
+  morkBookAtom& operator=(const morkBookAtom& other);
+};
+
+/*| FarBookAtom: this alternative format for book atoms was introduced
+**| in May 2000 in order to support finding atoms in hash tables without
+**| first copying the strings from original parsing buffers into a new
+**| atom format.  This was consuming too much time.  However, we can
+**| use morkFarBookAtom to stage a hash table query, as long as we then
+**| fix HashFormAndBody() and EqualFormAndBody() to use morkFarBookAtom
+**| correctly.
+**|
+**|| Note we do NOT intend that instances of morkFarBookAtom will ever
+**| be installed in hash tables, because this is not space efficient.
+**| We only expect to create temp instances for table lookups.
+|*/
+class morkFarBookAtom : public morkBookAtom { //
+  
+  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  // mork_change   mAtom_Change;    // how has this atom been changed?
+  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms
+
+  // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope 
+  // mork_aid       mBookAtom_Id;    // identity token for this shared atom
+  
+public:
+  mork_cscode   mFarBookAtom_Form;      // charset format encoding
+  mork_size     mFarBookAtom_Size;      // size of content vector
+  mork_u1*      mFarBookAtom_Body;      // bytes are elsewere, out of line
+
+public: // empty construction does nothing
+  morkFarBookAtom() { }
+  void InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf,
+    mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid);
+  
+private: // copying is not allowed
+  morkFarBookAtom(const morkFarBookAtom& other);
+  morkFarBookAtom& operator=(const morkFarBookAtom& other);
+};
+
+/*| WeeBookAtom: .
+|*/
+class morkWeeBookAtom : public morkBookAtom { //
+  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  // mork_change   mAtom_Change;    // how has this atom been changed?
+  // mork_u1       mAtom_Size;      // only for atoms smaller than 256 bytes
+
+  // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope 
+  // mork_aid       mBookAtom_Id;    // identity token for this shared atom
+  
+public:
+  mork_u1     mWeeBookAtom_Body[ 1 ]; // 1st byte of immed content vector
+
+public: // empty construction does nothing
+  morkWeeBookAtom() { }
+  morkWeeBookAtom(mork_aid inAid);
+  
+  void InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf,
+    morkAtomSpace* ioSpace, mork_aid inAid);
+  
+  // allow extra trailing byte for a null byte:
+  static mork_size SizeForFill(mork_fill inFill)
+  { return sizeof(morkWeeBookAtom) + inFill; }
+
+private: // copying is not allowed
+  morkWeeBookAtom(const morkWeeBookAtom& other);
+  morkWeeBookAtom& operator=(const morkWeeBookAtom& other);
+};
+
+/*| BigBookAtom: .
+|*/
+class morkBigBookAtom : public morkBookAtom { //
+  
+  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  // mork_change   mAtom_Change;    // how has this atom been changed?
+  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms
+
+  // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope 
+  // mork_aid       mBookAtom_Id;    // identity token for this shared atom
+  
+public:
+  mork_cscode   mBigBookAtom_Form;      // charset format encoding
+  mork_size     mBigBookAtom_Size;      // size of content vector
+  mork_u1       mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector
+
+public: // empty construction does nothing
+  morkBigBookAtom() { }
+  void InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf,
+    mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid);
+  
+  // allow extra trailing byte for a null byte:
+  static mork_size SizeForFill(mork_fill inFill)
+  { return sizeof(morkBigBookAtom) + inFill; }
+
+private: // copying is not allowed
+  morkBigBookAtom(const morkBigBookAtom& other);
+  morkBigBookAtom& operator=(const morkBigBookAtom& other);
+};
+
+/*| MaxBookAtom: .
+|*/
+class morkMaxBookAtom : public morkBigBookAtom { //
+  
+  // mork_u1       mAtom_Kind;      // identifies a specific atom subclass
+  // mork_u1       mAtom_CellUses;  // number of persistent uses in a cell
+  // mork_change   mAtom_Change;    // how has this atom been changed?
+  // mork_u1       mAtom_Size;      // NOT USED IN "BIG" format atoms
+
+  // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope 
+  // mork_aid       mBookAtom_Id;    // identity token for this shared atom
+
+  // mork_cscode   mBigBookAtom_Form;      // charset format encoding
+  // mork_size     mBigBookAtom_Size;      // size of content vector
+  // mork_u1       mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector
+  
+public:
+  mork_u1 mMaxBookAtom_Body[ morkBookAtom_kMaxBodySize + 3 ]; // max bytes
+
+public: // empty construction does nothing
+  morkMaxBookAtom() { }
+  void InitMaxBookAtom(morkEnv* ev, const morkBuf& inBuf,
+    mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid)
+  { this->InitBigBookAtom(ev, inBuf, inForm, ioSpace, inAid); }
+
+private: // copying is not allowed
+  morkMaxBookAtom(const morkMaxBookAtom& other);
+  morkMaxBookAtom& operator=(const morkMaxBookAtom& other);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKATOM_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkAtomMap.cpp
@@ -0,0 +1,469 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkAtomAidMap::CloseMorkNode(morkEnv* ev) // CloseAtomAidMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseAtomAidMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkAtomAidMap::~morkAtomAidMap() // assert CloseAtomAidMap() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+
+/*public non-poly*/
+morkAtomAidMap::morkAtomAidMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+#ifdef MORK_ENABLE_PROBE_MAPS
+: morkProbeMap(ev, inUsage,  ioHeap,
+  /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0,
+  ioSlotHeap, morkAtomAidMap_kStartSlotCount, 
+  /*inZeroIsClearKey*/ morkBool_kTrue)
+#else /*MORK_ENABLE_PROBE_MAPS*/
+: morkMap(ev, inUsage,  ioHeap,
+  /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0,
+  morkAtomAidMap_kStartSlotCount, ioSlotHeap,
+  /*inHoldChanges*/ morkBool_kFalse)
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kAtomAidMap;
+}
+
+/*public non-poly*/ void
+morkAtomAidMap::CloseAtomAidMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+#ifdef MORK_ENABLE_PROBE_MAPS
+      this->CloseProbeMap(ev);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+      this->CloseMap(ev);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+
+  /*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+  morkAtomAidMap::MapTest(morkEnv* ev, const void* inMapKey,
+    const void* inAppKey) const
+  {
+    MORK_USED_1(ev);
+    const morkBookAtom* key = *(const morkBookAtom**) inMapKey;
+    if ( key )
+    {
+      mork_bool hit = key->EqualAid(*(const morkBookAtom**) inAppKey);
+      return ( hit ) ? morkTest_kHit : morkTest_kMiss;
+    }
+    else
+      return morkTest_kVoid;
+  }
+
+  /*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+  morkAtomAidMap::MapHash(morkEnv* ev, const void* inAppKey) const
+  {
+    const morkBookAtom* key = *(const morkBookAtom**) inAppKey;
+    if ( key )
+      return key->HashAid();
+    else
+    {
+      ev->NilPointerWarning();
+      return 0;
+    }
+  }
+
+  /*virtual*/ mork_u4 
+  morkAtomAidMap::ProbeMapHashMapKey(morkEnv* ev,
+    const void* inMapKey) const
+  {
+    const morkBookAtom* key = *(const morkBookAtom**) inMapKey;
+    if ( key )
+      return key->HashAid();
+    else
+    {
+      ev->NilPointerWarning();
+      return 0;
+    }
+  }
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  // { ===== begin morkMap poly interface =====
+  /*virtual*/ mork_bool // 
+  morkAtomAidMap::Equal(morkEnv* ev, const void* inKeyA,
+    const void* inKeyB) const
+  {
+    MORK_USED_1(ev);
+    return (*(const morkBookAtom**) inKeyA)->EqualAid(
+      *(const morkBookAtom**) inKeyB);
+  }
+
+  /*virtual*/ mork_u4 // 
+  morkAtomAidMap::Hash(morkEnv* ev, const void* inKey) const
+  {
+    MORK_USED_1(ev);
+    return (*(const morkBookAtom**) inKey)->HashAid();
+  }
+  // } ===== end morkMap poly interface =====
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+
+mork_bool
+morkAtomAidMap::AddAtom(morkEnv* ev, morkBookAtom* ioAtom)
+{
+  if ( ev->Good() )
+  {
+#ifdef MORK_ENABLE_PROBE_MAPS
+    this->MapAtPut(ev, &ioAtom, /*val*/ (void*) 0, 
+      /*key*/ (void*) 0, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+    this->Put(ev, &ioAtom, /*val*/ (void*) 0, 
+      /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  }
+  return ev->Good();
+}
+
+morkBookAtom*
+morkAtomAidMap::CutAtom(morkEnv* ev, const morkBookAtom* inAtom)
+{
+  morkBookAtom* oldKey = 0;
+  
+#ifdef MORK_ENABLE_PROBE_MAPS
+  MORK_USED_1(inAtom);
+  morkProbeMap::ProbeMapCutError(ev);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  this->Cut(ev, &inAtom, &oldKey, /*val*/ (void*) 0,
+    (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+    
+  return oldKey;
+}
+
+morkBookAtom*
+morkAtomAidMap::GetAtom(morkEnv* ev, const morkBookAtom* inAtom)
+{
+  morkBookAtom* key = 0; // old val in the map
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+  this->MapAt(ev, &inAtom, &key, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  this->Get(ev, &inAtom, &key, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  
+  return key;
+}
+
+morkBookAtom*
+morkAtomAidMap::GetAid(morkEnv* ev, mork_aid inAid)
+{
+  morkWeeBookAtom weeAtom(inAid);
+  morkBookAtom* key = &weeAtom; // we need a pointer
+  morkBookAtom* oldKey = 0; // old key in the map
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+  this->MapAt(ev, &key, &oldKey, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  this->Get(ev, &key, &oldKey, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  
+  return oldKey;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkAtomBodyMap::CloseMorkNode(morkEnv* ev) // CloseAtomBodyMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseAtomBodyMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkAtomBodyMap::~morkAtomBodyMap() // assert CloseAtomBodyMap() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+
+/*public non-poly*/
+morkAtomBodyMap::morkAtomBodyMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+#ifdef MORK_ENABLE_PROBE_MAPS
+: morkProbeMap(ev, inUsage,  ioHeap,
+  /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0,
+  ioSlotHeap, morkAtomBodyMap_kStartSlotCount, 
+  /*inZeroIsClearKey*/ morkBool_kTrue)
+#else /*MORK_ENABLE_PROBE_MAPS*/
+: morkMap(ev, inUsage,  ioHeap,
+  /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0,
+  morkAtomBodyMap_kStartSlotCount, ioSlotHeap,
+  /*inHoldChanges*/ morkBool_kFalse)
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kAtomBodyMap;
+}
+
+/*public non-poly*/ void
+morkAtomBodyMap::CloseAtomBodyMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+#ifdef MORK_ENABLE_PROBE_MAPS
+      this->CloseProbeMap(ev);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+      this->CloseMap(ev);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+#ifdef MORK_ENABLE_PROBE_MAPS
+
+  /*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+  morkAtomBodyMap::MapTest(morkEnv* ev, const void* inMapKey,
+    const void* inAppKey) const
+  {
+    const morkBookAtom* key = *(const morkBookAtom**) inMapKey;
+    if ( key )
+    {
+      return ( key->EqualFormAndBody(ev, *(const morkBookAtom**) inAppKey) ) ?
+        morkTest_kHit : morkTest_kMiss;
+    }
+    else
+      return morkTest_kVoid;
+  }
+
+  /*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+  morkAtomBodyMap::MapHash(morkEnv* ev, const void* inAppKey) const
+  {
+    const morkBookAtom* key = *(const morkBookAtom**) inAppKey;
+    if ( key )
+      return key->HashFormAndBody(ev);
+    else
+      return 0;
+  }
+
+  /*virtual*/ mork_u4 
+  morkAtomBodyMap::ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const
+  {
+    const morkBookAtom* key = *(const morkBookAtom**) inMapKey;
+    if ( key )
+      return key->HashFormAndBody(ev);
+    else
+      return 0;
+  }
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  // { ===== begin morkMap poly interface =====
+  /*virtual*/ mork_bool // 
+  morkAtomBodyMap::Equal(morkEnv* ev, const void* inKeyA,
+    const void* inKeyB) const
+  {
+    return (*(const morkBookAtom**) inKeyA)->EqualFormAndBody(ev,
+      *(const morkBookAtom**) inKeyB);
+  }
+
+  /*virtual*/ mork_u4 // 
+  morkAtomBodyMap::Hash(morkEnv* ev, const void* inKey) const
+  {
+    return (*(const morkBookAtom**) inKey)->HashFormAndBody(ev);
+  }
+  // } ===== end morkMap poly interface =====
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+
+mork_bool
+morkAtomBodyMap::AddAtom(morkEnv* ev, morkBookAtom* ioAtom)
+{
+  if ( ev->Good() )
+  {
+#ifdef MORK_ENABLE_PROBE_MAPS
+    this->MapAtPut(ev, &ioAtom, /*val*/ (void*) 0, 
+      /*key*/ (void*) 0, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+    this->Put(ev, &ioAtom, /*val*/ (void*) 0, 
+      /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  }
+  return ev->Good();
+}
+
+morkBookAtom*
+morkAtomBodyMap::CutAtom(morkEnv* ev, const morkBookAtom* inAtom)
+{
+  morkBookAtom* oldKey = 0;
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+  MORK_USED_1(inAtom);
+  morkProbeMap::ProbeMapCutError(ev);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  this->Cut(ev, &inAtom, &oldKey, /*val*/ (void*) 0,
+    (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+    
+  return oldKey;
+}
+
+morkBookAtom*
+morkAtomBodyMap::GetAtom(morkEnv* ev, const morkBookAtom* inAtom)
+{
+  morkBookAtom* key = 0; // old val in the map
+#ifdef MORK_ENABLE_PROBE_MAPS
+  this->MapAt(ev, &inAtom, &key, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  this->Get(ev, &inAtom, &key, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  
+  return key;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+morkAtomRowMap::~morkAtomRowMap()
+{
+}
+
+// I changed to sizeof(mork_ip) from sizeof(mork_aid) to fix a crash on
+// 64 bit machines.  I am not sure it was the right way to fix the problem,
+// but it does stop the crash.  Perhaps we should be using the
+// morkPointerMap instead?
+morkAtomRowMap::morkAtomRowMap(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_column inIndexColumn)
+  : morkIntMap(ev, inUsage, sizeof(mork_ip), ioHeap, ioSlotHeap,
+    /*inHoldChanges*/ morkBool_kFalse)
+, mAtomRowMap_IndexColumn( inIndexColumn )
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kAtomRowMap;
+}
+
+void morkAtomRowMap::AddRow(morkEnv* ev, morkRow* ioRow)
+// add ioRow only if it contains a cell in mAtomRowMap_IndexColumn. 
+{
+  mork_aid aid = ioRow->GetCellAtomAid(ev, mAtomRowMap_IndexColumn);
+  if ( aid )
+    this->AddAid(ev, aid, ioRow);
+}
+
+void morkAtomRowMap::CutRow(morkEnv* ev, morkRow* ioRow)
+// cut ioRow only if it contains a cell in mAtomRowMap_IndexColumn. 
+{
+  mork_aid aid = ioRow->GetCellAtomAid(ev, mAtomRowMap_IndexColumn);
+  if ( aid )
+    this->CutAid(ev, aid);
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkAtomMap.h
@@ -0,0 +1,397 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKATOMMAP_
+#define _MORKATOMMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kAtomAidMap  /*i*/ 0x6141 /* ascii 'aA' */
+
+#define morkAtomAidMap_kStartSlotCount 23
+
+/*| morkAtomAidMap: keys of morkBookAtom organized by atom ID
+|*/
+#ifdef MORK_ENABLE_PROBE_MAPS
+class morkAtomAidMap : public morkProbeMap { // for mapping tokens to maps
+#else /*MORK_ENABLE_PROBE_MAPS*/
+class morkAtomAidMap : public morkMap { // for mapping tokens to maps
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseAtomAidMap() only if open
+  virtual ~morkAtomAidMap(); // assert that CloseAtomAidMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkAtomAidMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseAtomAidMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsAtomAidMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kAtomAidMap; }
+// } ===== end morkNode methods =====
+
+public:
+#ifdef MORK_ENABLE_PROBE_MAPS
+  // { ===== begin morkProbeMap methods =====
+  virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+  MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const;
+
+  virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+  MapHash(morkEnv* ev, const void* inAppKey) const;
+
+  virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const;
+
+  // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+
+  // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+  //   void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+
+  // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+  //   const void* inAppKey, const void* inAppVal, // (key,val) outside map
+  //   void* outMapKey, void* outMapVal);      // (key,val) inside map
+
+  // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+  //   const void* inMapKey, const void* inMapVal, // (key,val) inside map
+  //   void* outAppKey, void* outAppVal) const;    // (key,val) outside map
+  // } ===== end morkProbeMap methods =====
+#else /*MORK_ENABLE_PROBE_MAPS*/
+// { ===== begin morkMap poly interface =====
+  virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b)
+  Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const;
+  // implemented using morkBookAtom::HashAid()
+
+  virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b)
+  Hash(morkEnv* ev, const void* inKey) const;
+  // implemented using morkBookAtom::EqualAid()
+// } ===== end morkMap poly interface =====
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+public: // other map methods
+
+  mork_bool      AddAtom(morkEnv* ev, morkBookAtom* ioAtom);
+  // AddAtom() returns ev->Good()
+
+  morkBookAtom*  CutAtom(morkEnv* ev, const morkBookAtom* inAtom);
+  // CutAtom() returns the atom removed equal to inAtom, if there was one
+  
+  morkBookAtom*  GetAtom(morkEnv* ev, const morkBookAtom* inAtom);
+  // GetAtom() returns the atom equal to inAtom, or else nil
+
+  morkBookAtom*  GetAid(morkEnv* ev, mork_aid inAid);
+  // GetAid() returns the atom equal to inAid, or else nil
+
+  // note the atoms are owned elsewhere, usuall by morkAtomSpace
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakAtomAidMap(morkAtomAidMap* me,
+    morkEnv* ev, morkAtomAidMap** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongAtomAidMap(morkAtomAidMap* me,
+    morkEnv* ev, morkAtomAidMap** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+class morkAtomAidMapIter: public morkProbeMapIter { // typesafe wrapper class
+#else /*MORK_ENABLE_PROBE_MAPS*/
+class morkAtomAidMapIter: public morkMapIter { // typesafe wrapper class
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+public:
+#ifdef MORK_ENABLE_PROBE_MAPS
+  morkAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap)
+  : morkProbeMapIter(ev, ioMap) { }
+ 
+  morkAtomAidMapIter( ) : morkProbeMapIter()  { }
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  morkAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkAtomAidMapIter( ) : morkMapIter()  { }
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+  void InitAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change* FirstAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+  { return this->First(ev, outAtomPtr, /*val*/ (void*) 0); }
+  
+  mork_change* NextAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+  { return this->Next(ev, outAtomPtr, /*val*/ (void*) 0); }
+  
+  mork_change* HereAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+  { return this->Here(ev, outAtomPtr, /*val*/ (void*) 0); }
+  
+  mork_change* CutHereAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+  { return this->CutHere(ev, outAtomPtr, /*val*/ (void*) 0); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kAtomBodyMap  /*i*/ 0x6142 /* ascii 'aB' */
+
+#define morkAtomBodyMap_kStartSlotCount 23
+
+/*| morkAtomBodyMap: keys of morkBookAtom organized by body bytes
+|*/
+#ifdef MORK_ENABLE_PROBE_MAPS
+class morkAtomBodyMap : public morkProbeMap { // for mapping tokens to maps
+#else /*MORK_ENABLE_PROBE_MAPS*/
+class morkAtomBodyMap : public morkMap { // for mapping tokens to maps
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseAtomBodyMap() only if open
+  virtual ~morkAtomBodyMap(); // assert CloseAtomBodyMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkAtomBodyMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseAtomBodyMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsAtomBodyMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kAtomBodyMap; }
+// } ===== end morkNode methods =====
+
+public:
+#ifdef MORK_ENABLE_PROBE_MAPS
+  // { ===== begin morkProbeMap methods =====
+  virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+  MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const;
+
+  virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+  MapHash(morkEnv* ev, const void* inAppKey) const;
+
+  virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const;
+
+  // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+
+  // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+  //   void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+
+  // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+  //   const void* inAppKey, const void* inAppVal, // (key,val) outside map
+  //   void* outMapKey, void* outMapVal);      // (key,val) inside map
+
+  // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+  //   const void* inMapKey, const void* inMapVal, // (key,val) inside map
+  //   void* outAppKey, void* outAppVal) const;    // (key,val) outside map
+  // } ===== end morkProbeMap methods =====
+#else /*MORK_ENABLE_PROBE_MAPS*/
+// { ===== begin morkMap poly interface =====
+  virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b)
+  Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const;
+  // implemented using morkBookAtom::EqualFormAndBody()
+
+  virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b)
+  Hash(morkEnv* ev, const void* inKey) const;
+  // implemented using morkBookAtom::HashFormAndBody()
+// } ===== end morkMap poly interface =====
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+public: // other map methods
+
+  mork_bool      AddAtom(morkEnv* ev, morkBookAtom* ioAtom);
+  // AddAtom() returns ev->Good()
+
+  morkBookAtom*  CutAtom(morkEnv* ev, const morkBookAtom* inAtom);
+  // CutAtom() returns the atom removed equal to inAtom, if there was one
+  
+  morkBookAtom*  GetAtom(morkEnv* ev, const morkBookAtom* inAtom);
+  // GetAtom() returns the atom equal to inAtom, or else nil
+
+  // note the atoms are owned elsewhere, usuall by morkAtomSpace
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakAtomBodyMap(morkAtomBodyMap* me,
+    morkEnv* ev, morkAtomBodyMap** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongAtomBodyMap(morkAtomBodyMap* me,
+    morkEnv* ev, morkAtomBodyMap** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+class morkAtomBodyMapIter: public morkProbeMapIter{ // typesafe wrapper class
+#else /*MORK_ENABLE_PROBE_MAPS*/
+class morkAtomBodyMapIter: public morkMapIter{ // typesafe wrapper class
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+public:
+#ifdef MORK_ENABLE_PROBE_MAPS
+  morkAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap)
+  : morkProbeMapIter(ev, ioMap) { }
+ 
+  morkAtomBodyMapIter( ) : morkProbeMapIter()  { }
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  morkAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkAtomBodyMapIter( ) : morkMapIter()  { }
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  
+  void InitAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change* FirstAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+  { return this->First(ev, outAtomPtr, /*val*/ (void*) 0); }
+  
+  mork_change* NextAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+  { return this->Next(ev, outAtomPtr, /*val*/ (void*) 0); }
+  
+  mork_change* HereAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+  { return this->Here(ev, outAtomPtr, /*val*/ (void*) 0); }
+  
+  mork_change* CutHereAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+  { return this->CutHere(ev, outAtomPtr, /*val*/ (void*) 0); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kAtomRowMap  /*i*/ 0x6152 /* ascii 'aR' */
+
+/*| morkAtomRowMap: maps morkAtom* -> morkRow*
+|*/
+class morkAtomRowMap : public morkIntMap { // for mapping atoms to rows
+
+public:
+  mork_column mAtomRowMap_IndexColumn; // row column being indexed
+
+public:
+
+  virtual ~morkAtomRowMap();
+  morkAtomRowMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_column inIndexColumn);
+
+public: // adding and cutting from morkRow instance candidate
+
+  void  AddRow(morkEnv* ev, morkRow* ioRow);
+  // add ioRow only if it contains a cell in mAtomRowMap_IndexColumn. 
+
+  void  CutRow(morkEnv* ev, morkRow* ioRow);
+  // cut ioRow only if it contains a cell in mAtomRowMap_IndexColumn. 
+
+public: // other map methods
+
+  mork_bool  AddAid(morkEnv* ev, mork_aid inAid, morkRow* ioRow)
+  { return this->AddInt(ev, inAid, ioRow); }
+  // the AddAid() boolean return equals ev->Good().
+
+  mork_bool  CutAid(morkEnv* ev, mork_aid inAid)
+  { return this->CutInt(ev, inAid); }
+  // The CutAid() boolean return indicates whether removal happened. 
+  
+  morkRow*   GetAid(morkEnv* ev, mork_aid inAid)
+  { return (morkRow*) this->GetInt(ev, inAid); }
+  // Note the returned space does NOT have an increase in refcount for this.
+  
+public: // dynamic type identification
+  mork_bool IsAtomRowMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kAtomRowMap; }
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakAtomRowMap(morkAtomRowMap* me,
+    morkEnv* ev, morkAtomRowMap** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongAtomRowMap(morkAtomRowMap* me,
+    morkEnv* ev, morkAtomRowMap** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+};
+
+class morkAtomRowMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+  morkAtomRowMapIter(morkEnv* ev, morkAtomRowMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkAtomRowMapIter( ) : morkMapIter()  { }
+  void InitAtomRowMapIter(morkEnv* ev, morkAtomRowMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change*
+  FirstAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow)
+  { return this->First(ev, outAtom, outRow); }
+  
+  mork_change*
+  NextAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow)
+  { return this->Next(ev, outAtom, outRow); }
+  
+  mork_change*
+  HereAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow)
+  { return this->Here(ev, outAtom, outRow); }
+  
+  mork_change*
+  CutHereAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow)
+  { return this->CutHere(ev, outAtom, outRow); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKATOMMAP_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkAtomSpace.cpp
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkAtomSpace::CloseMorkNode(morkEnv* ev) // CloseAtomSpace() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseAtomSpace(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkAtomSpace::~morkAtomSpace() // assert CloseAtomSpace() executed earlier
+{
+  MORK_ASSERT(mAtomSpace_HighUnderId==0);
+  MORK_ASSERT(mAtomSpace_HighOverId==0);
+  MORK_ASSERT(this->IsShutNode());
+  MORK_ASSERT(mAtomSpace_AtomAids.IsShutNode());
+  MORK_ASSERT(mAtomSpace_AtomBodies.IsShutNode());
+}
+
+/*public non-poly*/
+morkAtomSpace::morkAtomSpace(morkEnv* ev, const morkUsage& inUsage,
+  mork_scope inScope, morkStore* ioStore,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap)
+, mAtomSpace_HighUnderId( morkAtomSpace_kMinUnderId )
+, mAtomSpace_HighOverId( morkAtomSpace_kMinOverId )
+, mAtomSpace_AtomAids(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
+, mAtomSpace_AtomBodies(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
+{
+  // the morkSpace base constructor handles any dirty propagation
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kAtomSpace;
+}
+
+/*public non-poly*/ void
+morkAtomSpace::CloseAtomSpace(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mAtomSpace_AtomBodies.CloseMorkNode(ev);
+      morkStore* store = mSpace_Store;
+      if ( store )
+        this->CutAllAtoms(ev, &store->mStore_Pool);
+      
+      mAtomSpace_AtomAids.CloseMorkNode(ev);
+      this->CloseSpace(ev);
+      mAtomSpace_HighUnderId = 0;
+      mAtomSpace_HighOverId = 0;
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkAtomSpace::NonAtomSpaceTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkAtomSpace");
+}
+
+mork_num
+morkAtomSpace::CutAllAtoms(morkEnv* ev, morkPool* ioPool)
+{
+#ifdef MORK_ENABLE_ZONE_ARENAS
+  MORK_USED_2(ev, ioPool);
+  return 0;
+#else /*MORK_ENABLE_ZONE_ARENAS*/
+  if ( this->IsAtomSpaceClean() )
+    this->MaybeDirtyStoreAndSpace();
+  
+  mork_num outSlots = mAtomSpace_AtomAids.MapFill();
+  morkBookAtom* a = 0; // old key atom in the map
+  
+  morkStore* store = mSpace_Store;
+  mork_change* c = 0;
+  morkAtomAidMapIter i(ev, &mAtomSpace_AtomAids);
+  for ( c = i.FirstAtom(ev, &a); c ; c = i.NextAtom(ev, &a) )
+  {
+    if ( a )
+      ioPool->ZapAtom(ev, a, &store->mStore_Zone);
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+    // do not cut anything from the map
+#else /*MORK_ENABLE_PROBE_MAPS*/
+    i.CutHereAtom(ev, /*key*/ (morkBookAtom**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  }
+  
+  return outSlots;
+#endif /*MORK_ENABLE_ZONE_ARENAS*/
+}
+
+
+morkBookAtom*
+morkAtomSpace::MakeBookAtomCopyWithAid(morkEnv* ev,
+   const morkFarBookAtom& inAtom,  mork_aid inAid)
+// Make copy of inAtom and put it in both maps, using specified ID.
+{
+  morkBookAtom* outAtom = 0;
+  morkStore* store = mSpace_Store;
+  if ( ev->Good() && store )
+  {
+    morkPool* pool = this->GetSpaceStorePool();
+    outAtom = pool->NewFarBookAtomCopy(ev, inAtom, &store->mStore_Zone);
+    if ( outAtom )
+    {
+      if ( store->mStore_CanDirty )
+      {
+        outAtom->SetAtomDirty();
+        if ( this->IsAtomSpaceClean() )
+          this->MaybeDirtyStoreAndSpace();
+      }
+  
+      outAtom->mBookAtom_Id = inAid;
+      outAtom->mBookAtom_Space = this;
+      mAtomSpace_AtomAids.AddAtom(ev, outAtom);
+      mAtomSpace_AtomBodies.AddAtom(ev, outAtom);
+      if ( this->SpaceScope() == morkAtomSpace_kColumnScope )
+        outAtom->MakeCellUseForever(ev);
+
+      if ( mAtomSpace_HighUnderId <= inAid )
+        mAtomSpace_HighUnderId = inAid + 1;
+    }
+  }
+  return outAtom;
+}
+
+morkBookAtom*
+morkAtomSpace::MakeBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom)
+// make copy of inAtom and put it in both maps, using a new ID as needed.
+{
+  morkBookAtom* outAtom = 0;
+  morkStore* store = mSpace_Store;
+  if ( ev->Good() && store )
+  {
+    if ( store->mStore_CanAutoAssignAtomIdentity )
+    {
+      morkPool* pool = this->GetSpaceStorePool();
+      morkBookAtom* atom = pool->NewFarBookAtomCopy(ev, inAtom, &mSpace_Store->mStore_Zone);
+      if ( atom )
+      {
+        mork_aid id = this->MakeNewAtomId(ev, atom);
+        if ( id )
+        {
+          if ( store->mStore_CanDirty )
+          {
+            atom->SetAtomDirty();
+            if ( this->IsAtomSpaceClean() )
+              this->MaybeDirtyStoreAndSpace();
+          }
+            
+          outAtom = atom; 
+          atom->mBookAtom_Space = this;
+          mAtomSpace_AtomAids.AddAtom(ev, atom);
+          mAtomSpace_AtomBodies.AddAtom(ev, atom);
+          if ( this->SpaceScope() == morkAtomSpace_kColumnScope )
+            outAtom->MakeCellUseForever(ev);
+        }
+        else
+          pool->ZapAtom(ev, atom, &mSpace_Store->mStore_Zone);
+      }
+    }
+    else
+      mSpace_Store->CannotAutoAssignAtomIdentityError(ev);
+  }
+  return outAtom;
+}
+
+
+mork_aid
+morkAtomSpace::MakeNewAtomId(morkEnv* ev, morkBookAtom* ioAtom)
+{
+  mork_aid outAid = 0;
+  mork_tid id = mAtomSpace_HighUnderId;
+  mork_num count = 8; // try up to eight times
+  
+  while ( !outAid && count ) // still trying to find an unused table ID?
+  {
+    --count;
+    ioAtom->mBookAtom_Id = id;
+    if ( !mAtomSpace_AtomAids.GetAtom(ev, ioAtom) )
+      outAid = id;
+    else
+    {
+      MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems
+      ++id;
+    }
+  }
+  
+  mAtomSpace_HighUnderId = id + 1;
+  return outAid;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+morkAtomSpaceMap::~morkAtomSpaceMap()
+{
+}
+
+morkAtomSpaceMap::morkAtomSpaceMap(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+  : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kAtomSpaceMap;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkAtomSpace.h
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKATOMSPACE_
+#define _MORKATOMSPACE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| kMinUnderId: the smallest ID we auto-assign to the 'under' namespace
+**| reserved for tokens expected to occur very frequently, such as the names
+**| of columns.  We reserve single byte ids in the ASCII range to correspond
+**| one-to-one to those tokens consisting single ASCII characters (so that
+**| this assignment is always known and constant).  So we start at 0x80, and
+**| then reserve the upper half of two hex digit ids and all the three hex
+**| digit IDs for the 'under' namespace for common tokens.
+|*/
+#define morkAtomSpace_kMinUnderId 0x80   /* low 7 bits mean byte tokens */
+
+#define morkAtomSpace_kMaxSevenBitAid 0x7F  /* low seven bit integer ID */
+
+/*| kMinOverId: the smallest ID we auto-assign to the 'over' namespace that
+**| might include very large numbers of tokens that are used infrequently,
+**| so that we care less whether the shortest hex representation is used.
+**| So we start all IDs for 'over' category tokens at a value range that
+**| needs at least four hex digits, so we can reserve three hex digits and
+**| shorter for more commonly occuring tokens in the 'under' category.
+|*/
+#define morkAtomSpace_kMinOverId 0x1000  /* using at least four hex bytes */
+
+#define morkDerived_kAtomSpace  /*i*/ 0x6153 /* ascii 'aS' */
+
+#define morkAtomSpace_kColumnScope ((mork_scope) 'c') /* column scope is forever */
+
+/*| morkAtomSpace:
+|*/
+class morkAtomSpace : public morkSpace { // 
+
+// public: // slots inherited from morkSpace (meant to inform only)
+  // nsIMdbHeap*    mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+  
+  // morkStore*  mSpace_Store; // weak ref to containing store
+  
+  // mork_bool   mSpace_DoAutoIDs;    // whether db should assign member IDs
+  // mork_bool   mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs
+  // mork_u1     mSpace_Pad[ 2 ];     // pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+
+  mork_aid         mAtomSpace_HighUnderId; // high ID in 'under' range
+  mork_aid         mAtomSpace_HighOverId;  // high ID in 'over' range
+  
+  morkAtomAidMap   mAtomSpace_AtomAids; // all atoms in space by ID
+  morkAtomBodyMap  mAtomSpace_AtomBodies; // all atoms in space by body
+
+public: // more specific dirty methods for atom space:
+  void SetAtomSpaceDirty() { this->SetNodeDirty(); }
+  void SetAtomSpaceClean() { this->SetNodeClean(); }
+  
+  mork_bool IsAtomSpaceClean() const { return this->IsNodeClean(); }
+  mork_bool IsAtomSpaceDirty() const { return this->IsNodeDirty(); }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseAtomSpace() only if open
+  virtual ~morkAtomSpace(); // assert that CloseAtomSpace() executed earlier
+  
+public: // morkMap construction & destruction
+  morkAtomSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope, 
+    morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseAtomSpace(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsAtomSpace() const
+  { return IsNode() && mNode_Derived == morkDerived_kAtomSpace; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  void NonAtomSpaceTypeError(morkEnv* ev);
+
+public: // setup
+
+  mork_bool MarkAllAtomSpaceContentDirty(morkEnv* ev);
+  // MarkAllAtomSpaceContentDirty() visits every space object and marks 
+  // them dirty, including every table, row, cell, and atom.  The return
+  // equals ev->Good(), to show whether any error happened.  This method is
+  // intended for use in the beginning of a "compress commit" which writes
+  // all store content, whether dirty or not.  We dirty everything first so
+  // that later iterations over content can mark things clean as they are
+  // written, and organize the process of serialization so that objects are
+  // written only at need (because of being dirty).
+
+public: // other space methods
+
+  // void ReserveColumnAidCount(mork_count inCount)
+  // {
+  //   mAtomSpace_HighUnderId = morkAtomSpace_kMinUnderId + inCount;
+  //   mAtomSpace_HighOverId = morkAtomSpace_kMinOverId + inCount;
+  // }
+
+  mork_num CutAllAtoms(morkEnv* ev, morkPool* ioPool);
+  // CutAllAtoms() puts all the atoms back in the pool.
+  
+  morkBookAtom* MakeBookAtomCopyWithAid(morkEnv* ev,
+     const morkFarBookAtom& inAtom,  mork_aid inAid);
+  // Make copy of inAtom and put it in both maps, using specified ID.
+  
+  morkBookAtom* MakeBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom);
+  // Make copy of inAtom and put it in both maps, using a new ID as needed.
+
+  mork_aid MakeNewAtomId(morkEnv* ev, morkBookAtom* ioAtom);
+  // generate an unused atom id.
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakAtomSpace(morkAtomSpace* me,
+    morkEnv* ev, morkAtomSpace** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongAtomSpace(morkAtomSpace* me,
+    morkEnv* ev, morkAtomSpace** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kAtomSpaceMap  /*i*/ 0x615A /* ascii 'aZ' */
+
+/*| morkAtomSpaceMap: maps mork_scope -> morkAtomSpace
+|*/
+class morkAtomSpaceMap : public morkNodeMap { // for mapping tokens to tables
+
+public:
+
+  virtual ~morkAtomSpaceMap();
+  morkAtomSpaceMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+
+public: // other map methods
+
+  mork_bool  AddAtomSpace(morkEnv* ev, morkAtomSpace* ioAtomSpace)
+  { return this->AddNode(ev, ioAtomSpace->SpaceScope(), ioAtomSpace); }
+  // the AddAtomSpace() boolean return equals ev->Good().
+
+  mork_bool  CutAtomSpace(morkEnv* ev, mork_scope inScope)
+  { return this->CutNode(ev, inScope); }
+  // The CutAtomSpace() boolean return indicates whether removal happened. 
+  
+  morkAtomSpace*  GetAtomSpace(morkEnv* ev, mork_scope inScope)
+  { return (morkAtomSpace*) this->GetNode(ev, inScope); }
+  // Note the returned space does NOT have an increase in refcount for this.
+
+  mork_num CutAllAtomSpaces(morkEnv* ev)
+  { return this->CutAllNodes(ev); }
+  // CutAllAtomSpaces() releases all the referenced table values.
+};
+
+class morkAtomSpaceMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+  morkAtomSpaceMapIter(morkEnv* ev, morkAtomSpaceMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkAtomSpaceMapIter( ) : morkMapIter()  { }
+  void InitAtomSpaceMapIter(morkEnv* ev, morkAtomSpaceMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change*
+  FirstAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace)
+  { return this->First(ev, outScope, outAtomSpace); }
+  
+  mork_change*
+  NextAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace)
+  { return this->Next(ev, outScope, outAtomSpace); }
+  
+  mork_change*
+  HereAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace)
+  { return this->Here(ev, outScope, outAtomSpace); }
+  
+  mork_change*
+  CutHereAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace)
+  { return this->CutHere(ev, outScope, outAtomSpace); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKATOMSPACE_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkBead.cpp
@@ -0,0 +1,472 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKBEAD_
+#include "morkBead.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkBead::CloseMorkNode(morkEnv* ev) // CloseBead() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseBead(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkBead::~morkBead() // assert CloseBead() executed earlier
+{
+  MORK_ASSERT(mBead_Color==0 || mNode_Usage == morkUsage_kStack );
+}
+
+/*public non-poly*/
+morkBead::morkBead(mork_color inBeadColor)
+: morkNode( morkUsage_kStack )
+, mBead_Color( inBeadColor )
+{
+}
+
+/*public non-poly*/
+morkBead::morkBead(const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+  mork_color inBeadColor)
+: morkNode( inUsage, ioHeap )
+, mBead_Color( inBeadColor )
+{
+}
+
+/*public non-poly*/
+morkBead::morkBead(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, mork_color inBeadColor)
+: morkNode(ev, inUsage, ioHeap)
+, mBead_Color( inBeadColor )
+{
+  if ( ev->Good() )
+  {
+    if ( ev->Good() )
+      mNode_Derived = morkDerived_kBead;
+  }
+}
+
+/*public non-poly*/ void
+morkBead::CloseBead(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      if ( !this->IsShutNode() )
+      {
+        mBead_Color = 0;
+        this->MarkShut();
+      }
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkBeadMap::CloseMorkNode(morkEnv* ev) // CloseBeadMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseBeadMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkBeadMap::~morkBeadMap() // assert CloseBeadMap() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkBeadMap::morkBeadMap(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkMap(ev, inUsage, ioHeap, sizeof(morkBead*), /*inValSize*/ 0,
+  /*slotCount*/ 11, ioSlotHeap, /*holdChanges*/ morkBool_kFalse)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kBeadMap;
+}
+
+/*public non-poly*/ void
+morkBeadMap::CloseBeadMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      this->CutAllBeads(ev);
+      this->CloseMap(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+mork_bool
+morkBeadMap::AddBead(morkEnv* ev, morkBead* ioBead)
+  // the AddBead() boolean return equals ev->Good().
+{
+  if ( ioBead && ev->Good() )
+  {
+    morkBead* oldBead = 0; // old key in the map
+
+    mork_bool put = this->Put(ev, &ioBead, /*val*/ (void*) 0,
+      /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0);
+      
+    if ( put ) // replaced an existing key?
+    {
+      if ( oldBead != ioBead ) // new bead was not already in table?
+        ioBead->AddStrongRef(ev); // now there's another ref
+        
+      if ( oldBead && oldBead != ioBead ) // need to release old node?
+        oldBead->CutStrongRef(ev);
+    }
+    else
+      ioBead->AddStrongRef(ev); // another ref if not already in table
+  }
+  else if ( !ioBead )
+    ev->NilPointerError();
+    
+  return ev->Good();
+}
+
+mork_bool
+morkBeadMap::CutBead(morkEnv* ev, mork_color inColor)
+{
+  morkBead* oldBead = 0; // old key in the map
+  morkBead bead(inColor);
+  morkBead* key = &bead;
+  
+  mork_bool outCutNode = this->Cut(ev, &key, 
+    /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0);
+    
+  if ( oldBead )
+    oldBead->CutStrongRef(ev);
+  
+  bead.CloseBead(ev);
+  return outCutNode;
+}
+
+morkBead*
+morkBeadMap::GetBead(morkEnv* ev, mork_color inColor)
+  // Note the returned bead does NOT have an increase in refcount for this.
+{
+  morkBead* oldBead = 0; // old key in the map
+  morkBead bead(inColor);
+  morkBead* key = &bead;
+
+  this->Get(ev, &key, /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0);
+  
+  bead.CloseBead(ev);
+  return oldBead;
+}
+
+mork_num
+morkBeadMap::CutAllBeads(morkEnv* ev)
+  // CutAllBeads() releases all the referenced beads.
+{
+  mork_num outSlots = mMap_Slots;
+  
+  morkBeadMapIter i(ev, this);
+  morkBead* b = i.FirstBead(ev);
+
+  while ( b )
+  {
+    b->CutStrongRef(ev);
+    i.CutHereBead(ev);
+    b = i.NextBead(ev);
+  }
+  
+  return outSlots;
+}
+
+
+// { ===== begin morkMap poly interface =====
+/*virtual*/ mork_bool
+morkBeadMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const
+{
+  MORK_USED_1(ev);
+  return (*(const morkBead**) inKeyA)->BeadEqual(
+    *(const morkBead**) inKeyB);
+}
+
+/*virtual*/ mork_u4
+morkBeadMap::Hash(morkEnv* ev, const void* inKey) const
+{
+  MORK_USED_1(ev);
+    return (*(const morkBead**) inKey)->BeadHash();
+}
+// } ===== end morkMap poly interface =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+ 
+morkBead* morkBeadMapIter::FirstBead(morkEnv* ev)
+{
+  morkBead* bead = 0;
+  this->First(ev, &bead, /*val*/ (void*) 0);
+  return bead;
+}
+
+morkBead* morkBeadMapIter::NextBead(morkEnv* ev)
+{
+  morkBead* bead = 0;
+  this->Next(ev, &bead, /*val*/ (void*) 0);
+  return bead;
+}
+
+morkBead* morkBeadMapIter::HereBead(morkEnv* ev)
+{
+  morkBead* bead = 0;
+  this->Here(ev, &bead, /*val*/ (void*) 0);
+  return bead;
+}
+
+void morkBeadMapIter::CutHereBead(morkEnv* ev)
+{
+  this->CutHere(ev, /*key*/ (void*) 0, /*val*/ (void*) 0);
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkBeadProbeMap::CloseMorkNode(morkEnv* ev) // CloseBeadProbeMap() if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseBeadProbeMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkBeadProbeMap::~morkBeadProbeMap() // assert CloseBeadProbeMap() earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+
+/*public non-poly*/
+morkBeadProbeMap::morkBeadProbeMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkProbeMap(ev, inUsage, ioHeap,
+  /*inKeySize*/ sizeof(morkBead*), /*inValSize*/ 0,
+  ioSlotHeap, /*startSlotCount*/ 11, 
+  /*inZeroIsClearKey*/ morkBool_kTrue)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kBeadProbeMap;
+}
+
+/*public non-poly*/ void
+morkBeadProbeMap::CloseBeadProbeMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      this->CutAllBeads(ev);
+      this->CloseProbeMap(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+morkBeadProbeMap::MapTest(morkEnv* ev, const void* inMapKey,
+  const void* inAppKey) const
+{
+  MORK_USED_1(ev);
+  const morkBead* key = *(const morkBead**) inMapKey;
+  if ( key )
+  {
+    mork_bool hit = key->BeadEqual(*(const morkBead**) inAppKey);
+    return ( hit ) ? morkTest_kHit : morkTest_kMiss;
+  }
+  else
+    return morkTest_kVoid;
+}
+
+/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+morkBeadProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const
+{
+  const morkBead* key = *(const morkBead**) inAppKey;
+  if ( key )
+    return key->BeadHash();
+  else
+  {
+    ev->NilPointerWarning();
+    return 0;
+  }
+}
+
+/*virtual*/ mork_u4 
+morkBeadProbeMap::ProbeMapHashMapKey(morkEnv* ev,
+  const void* inMapKey) const
+{
+  const morkBead* key = *(const morkBead**) inMapKey;
+  if ( key )
+    return key->BeadHash();
+  else
+  {
+    ev->NilPointerWarning();
+    return 0;
+  }
+}
+
+mork_bool
+morkBeadProbeMap::AddBead(morkEnv* ev, morkBead* ioBead)
+{
+  if ( ioBead && ev->Good() )
+  {
+    morkBead* bead = 0; // old key in the map
+    
+    mork_bool put = this->MapAtPut(ev, &ioBead, /*val*/ (void*) 0, 
+      /*key*/ &bead, /*val*/ (void*) 0);
+          
+    if ( put ) // replaced an existing key?
+    {
+      if ( bead != ioBead ) // new bead was not already in table?
+        ioBead->AddStrongRef(ev); // now there's another ref
+        
+      if ( bead && bead != ioBead ) // need to release old node?
+        bead->CutStrongRef(ev);
+    }
+    else
+      ioBead->AddStrongRef(ev); // now there's another ref
+  }
+  else if ( !ioBead )
+    ev->NilPointerError();
+    
+  return ev->Good();
+}
+
+morkBead*
+morkBeadProbeMap::GetBead(morkEnv* ev, mork_color inColor)
+{
+  morkBead* oldBead = 0; // old key in the map
+  morkBead bead(inColor);
+  morkBead* key = &bead;
+
+  this->MapAt(ev, &key, &oldBead, /*val*/ (void*) 0);
+  
+  bead.CloseBead(ev);
+  return oldBead;
+}
+
+mork_num
+morkBeadProbeMap::CutAllBeads(morkEnv* ev)
+  // CutAllBeads() releases all the referenced bead values.
+{
+  mork_num outSlots = sMap_Slots;
+  
+  morkBeadProbeMapIter i(ev, this);
+  morkBead* b = i.FirstBead(ev);
+
+  while ( b )
+  {
+    b->CutStrongRef(ev);
+    b = i.NextBead(ev);
+  }
+  this->MapCutAll(ev);
+  
+  return outSlots;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkBead.h
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKBEAD_
+#define _MORKBEAD_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kBead   /*i*/ 0x426F /* ascii 'Bo' */
+
+/*| morkBead: subclass of morkNode that adds knowledge of db suite factory
+**| and containing port to those objects that are exposed as instances of
+**| nsIMdbBead in the public interface.
+|*/
+class morkBead : public morkNode { 
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*    mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+  
+public: // state is public because the entire Mork system is private
+
+  mork_color      mBead_Color;   // ID for this bead
+
+public: // Hash() and Equal() for bead maps are same for all subclasses:
+
+  mork_u4 BeadHash() const { return (mork_u4) mBead_Color; }
+  mork_bool BeadEqual(const morkBead* inBead) const
+  { return ( mBead_Color == inBead->mBead_Color); }
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseBead() only if open
+  virtual ~morkBead(); // assert that CloseBead() executed earlier
+  
+public: // special case for stack construction for map usage:
+  morkBead(mork_color inBeadColor); // stack-based bead instance
+  
+protected: // special case for morkObject:
+  morkBead(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+    mork_color inBeadColor);
+  
+public: // morkEnv construction & destruction
+  morkBead(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+     mork_color inBeadColor);
+  void CloseBead(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkBead(const morkBead& other);
+  morkBead& operator=(const morkBead& other);
+
+public: // dynamic type identification
+  mork_bool IsBead() const
+  { return IsNode() && mNode_Derived == morkDerived_kBead; }
+// } ===== end morkNode methods =====
+
+  // void NewNilHandleError(morkEnv* ev); // mBead_Handle is nil
+  
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakBead(morkBead* me,
+    morkEnv* ev, morkBead** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongBead(morkBead* me,
+    morkEnv* ev, morkBead** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kBeadMap  /*i*/ 0x744D /* ascii 'bM' */
+
+/*| morkBeadMap: maps bead -> bead (key only using mBead_Color)
+|*/
+class morkBeadMap : public morkMap {
+
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseBeadMap() only if open
+  virtual ~morkBeadMap(); // assert that CloseBeadMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkBeadMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseBeadMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsBeadMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kBeadMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+public:
+  virtual mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB)
+  Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const;
+
+  virtual mork_u4 // some integer function of *((mork_u4*) inKey)
+  Hash(morkEnv* ev, const void* inKey) const;
+// } ===== end morkMap poly interface =====
+
+public: // other map methods
+
+  mork_bool  AddBead(morkEnv* ev, morkBead* ioBead);
+  // the AddBead() boolean return equals ev->Good().
+
+  mork_bool  CutBead(morkEnv* ev, mork_color inColor);
+  // The CutBead() boolean return indicates whether removal happened. 
+  
+  morkBead*  GetBead(morkEnv* ev, mork_color inColor);
+  // Note the returned bead does NOT have an increase in refcount for this.
+
+  mork_num CutAllBeads(morkEnv* ev);
+  // CutAllBeads() releases all the referenced beads.
+};
+
+class morkBeadMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+  morkBeadMapIter(morkEnv* ev, morkBeadMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkBeadMapIter( ) : morkMapIter()  { }
+  void InitBeadMapIter(morkEnv* ev, morkBeadMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  morkBead* FirstBead(morkEnv* ev);
+  morkBead* NextBead(morkEnv* ev);
+  morkBead* HereBead(morkEnv* ev);
+  void      CutHereBead(morkEnv* ev);
+  
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kBeadProbeMap  /*i*/ 0x6D74 /* ascii 'mb' */
+
+/*| morkBeadProbeMap: maps bead -> bead (key only using mBead_Color)
+|*/
+class morkBeadProbeMap : public morkProbeMap {
+
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseBeadProbeMap() only if open
+  virtual ~morkBeadProbeMap(); // assert that CloseBeadProbeMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkBeadProbeMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseBeadProbeMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsBeadProbeMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kBeadProbeMap; }
+// } ===== end morkNode methods =====
+
+  // { ===== begin morkProbeMap methods =====
+public:
+  virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+  MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const;
+
+  virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+  MapHash(morkEnv* ev, const void* inAppKey) const;
+
+  virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const;
+
+  // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+
+  // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+  //   void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+
+  // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+  //   const void* inAppKey, const void* inAppVal, // (key,val) outside map
+  //   void* outMapKey, void* outMapVal);      // (key,val) inside map
+
+  // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+  //   const void* inMapKey, const void* inMapVal, // (key,val) inside map
+  //   void* outAppKey, void* outAppVal) const;    // (key,val) outside map
+  // } ===== end morkProbeMap methods =====
+
+public: // other map methods
+
+  mork_bool  AddBead(morkEnv* ev, morkBead* ioBead);
+  // the AddBead() boolean return equals ev->Good().
+  
+  morkBead*  GetBead(morkEnv* ev, mork_color inColor);
+  // Note the returned bead does NOT have an increase in refcount for this.
+
+  mork_num   CutAllBeads(morkEnv* ev);
+  // CutAllBeads() releases all the referenced bead values.
+};
+
+class morkBeadProbeMapIter: public morkProbeMapIter { // typesafe wrapper class
+
+public:
+  morkBeadProbeMapIter(morkEnv* ev, morkBeadProbeMap* ioMap)
+  : morkProbeMapIter(ev, ioMap) { }
+ 
+  morkBeadProbeMapIter( ) : morkProbeMapIter()  { }
+  void InitBeadProbeMapIter(morkEnv* ev, morkBeadProbeMap* ioMap)
+  { this->InitProbeMapIter(ev, ioMap); }
+   
+  morkBead* FirstBead(morkEnv* ev)
+  { return (morkBead*) this->IterFirstKey(ev); }
+  
+  morkBead* NextBead(morkEnv* ev)
+  { return (morkBead*) this->IterNextKey(ev); }
+  
+  morkBead* HereBead(morkEnv* ev)
+  { return (morkBead*) this->IterHereKey(ev); }
+  
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKBEAD_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkBlob.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*static*/ void
+morkBuf::NilBufBodyError(morkEnv* ev)
+{
+  ev->NewError("nil mBuf_Body");
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*static*/ void
+morkBlob::BlobFillOverSizeError(morkEnv* ev)
+{
+  ev->NewError("mBuf_Fill > mBlob_Size");
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+mork_bool
+morkBlob::GrowBlob(morkEnv* ev, nsIMdbHeap* ioHeap, mork_size inNewSize)
+{
+  if ( ioHeap )
+  {
+    if ( !mBuf_Body ) // no body? implies zero sized?
+      mBlob_Size = 0;
+      
+    if ( mBuf_Fill > mBlob_Size ) // fill more than size?
+    {
+      ev->NewWarning("mBuf_Fill > mBlob_Size");
+      mBuf_Fill = mBlob_Size;
+    }
+      
+    if ( inNewSize > mBlob_Size ) // need to allocate larger blob?
+    {
+      mork_u1* body = 0;
+      ioHeap->Alloc(ev->AsMdbEnv(), inNewSize, (void**) &body);
+      if ( body && ev->Good() )
+      {
+        void* oldBody = mBuf_Body;
+        if ( mBlob_Size ) // any old content to transfer?
+          MORK_MEMCPY(body, oldBody, mBlob_Size);
+        
+        mBlob_Size = inNewSize; // install new size
+        mBuf_Body = body; // install new body
+        
+        if ( oldBody ) // need to free old buffer body?
+          ioHeap->Free(ev->AsMdbEnv(), oldBody);
+      }
+    }
+  }
+  else
+    ev->NilPointerError();
+    
+  if ( ev->Good() && mBlob_Size < inNewSize )
+    ev->NewError("mBlob_Size < inNewSize");
+    
+  return ev->Good();
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+morkCoil::morkCoil(morkEnv* ev, nsIMdbHeap* ioHeap)
+{
+  mBuf_Body = 0;
+  mBuf_Fill = 0;
+  mBlob_Size = 0;
+  mText_Form = 0;
+  mCoil_Heap = ioHeap;
+  if ( !ioHeap )
+    ev->NilPointerError();
+}
+
+void
+morkCoil::CloseCoil(morkEnv* ev)
+{
+  void* body = mBuf_Body;
+  nsIMdbHeap* heap = mCoil_Heap;
+
+  mBuf_Body = 0;
+  mCoil_Heap = 0;
+  
+  if ( body && heap )
+  {
+    heap->Free(ev->AsMdbEnv(), body);
+  }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkBlob.h
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKBLOB_
+#define _MORKBLOB_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| Buf: the minimum needed to describe location and content length.
+**| This is typically only enough to read from this buffer, since
+**| one cannot write effectively without knowing the size of a buf.
+|*/
+class morkBuf { // subset of nsIMdbYarn slots
+public:
+  void*         mBuf_Body;  // space for holding any binary content
+  mork_fill     mBuf_Fill;  // logical content in Buf in bytes
+
+public:
+  morkBuf() { }
+  morkBuf(const void* ioBuf, mork_fill inFill)
+  : mBuf_Body((void*) ioBuf), mBuf_Fill(inFill) { }
+
+  void ClearBufFill() { mBuf_Fill = 0; }
+
+  static void NilBufBodyError(morkEnv* ev);
+
+private: // copying is not allowed
+  morkBuf(const morkBuf& other);
+  morkBuf& operator=(const morkBuf& other);
+};
+
+/*| Blob: a buffer with an associated size, to increase known buf info
+**| to include max capacity in addition to buf location and content.
+**| This form factor allows us to allocate a vector of such blobs,
+**| which can share the same managing heap stored elsewhere, and that
+**| is why we don't include a pointer to a heap in this blob class.
+|*/
+class morkBlob : public morkBuf { // greater subset of nsIMdbYarn slots
+
+  // void*         mBuf_Body;  // space for holding any binary content
+  // mdb_fill      mBuf_Fill;  // logical content in Buf in bytes
+public:
+  mork_size      mBlob_Size;  // physical size of Buf in bytes
+
+public:
+  morkBlob() { }
+  morkBlob(const void* ioBuf, mork_fill inFill, mork_size inSize)
+  : morkBuf(ioBuf, inFill), mBlob_Size(inSize) { }
+ 
+  static void BlobFillOverSizeError(morkEnv* ev);
+ 
+public:
+  mork_bool GrowBlob(morkEnv* ev, nsIMdbHeap* ioHeap,
+    mork_size inNewSize);
+
+private: // copying is not allowed
+  morkBlob(const morkBlob& other);
+  morkBlob& operator=(const morkBlob& other);
+  
+};
+
+/*| Text: a blob with an associated charset annotation, where the
+**| charset actually includes the general notion of typing, and not
+**| just a specification of character set alone; we want to permit
+**| arbitrary charset annotations for ad hoc binary types as well.
+**| (We avoid including a nsIMdbHeap pointer in morkText for the same
+**| reason morkBlob does: we want minimal size vectors of morkText.)
+|*/
+class morkText : public morkBlob { // greater subset of nsIMdbYarn slots
+
+  // void*         mBuf_Body;  // space for holding any binary content
+  // mdb_fill      mBuf_Fill;  // logical content in Buf in bytes
+  // mdb_size      mBlob_Size;  // physical size of Buf in bytes
+
+public:
+  mork_cscode    mText_Form;  // charset format encoding
+
+  morkText() { }
+
+private: // copying is not allowed
+  morkText(const morkText& other);
+  morkText& operator=(const morkText& other);
+};
+
+/*| Coil: a text with an associated nsIMdbHeap instance that provides
+**| all memory management for the space pointed to by mBuf_Body. (This
+**| was the hardest type to give a name in this small class hierarchy,
+**| because it's hard to characterize self-management of one's space.)
+**| A coil is a self-contained blob that knows how to grow itself as
+**| necessary to hold more content when necessary.  Coil descends from
+**| morkText to include the mText_Form slot, even though this won't be
+**| needed always, because we are not as concerned about the overall
+**| size of this particular Coil object (if we were concerned about
+**| the size of an array of Coil instances, we would not bother with
+**| a separate heap pointer for each of them).
+**|
+**|| A coil makes a good medium in which to stream content as a sink,
+**| so we will have a subclass of morkSink called morkCoil that
+**| will stream bytes into this self-contained coil object. The name
+**| of this morkCoil class derives more from this intended usage than
+**| from anything else.  The Mork code to parse db content will use
+**| coils with associated sinks to accumulate parsed strings.
+**|
+**|| Heap: this is the heap used for memory allocation.  This instance
+**| is NOT refcounted, since this coil always assumes the heap is held
+**| through a reference elsewhere (for example, through the same object
+**| that contains or holds the coil itself.  This lack of refcounting
+**| is consistent with the fact that morkCoil itself is not refcounted,
+**| and is not intended for use as a standalone object.
+|*/
+class morkCoil : public morkText { // self-managing text blob object
+
+  // void*         mBuf_Body;  // space for holding any binary content
+  // mdb_fill      mBuf_Fill;  // logical content in Buf in bytes
+  // mdb_size      mBlob_Size;  // physical size of Buf in bytes
+  // mdb_cscode    mText_Form;  // charset format encoding
+public:
+  nsIMdbHeap*      mCoil_Heap;  // storage manager for mBuf_Body pointer
+
+public:
+  morkCoil(morkEnv* ev, nsIMdbHeap* ioHeap);
+  
+  void CloseCoil(morkEnv* ev);
+
+  mork_bool GrowCoil(morkEnv* ev, mork_size inNewSize)
+  { return this->GrowBlob(ev, mCoil_Heap, inNewSize); }
+
+private: // copying is not allowed
+  morkCoil(const morkCoil& other);
+  morkCoil& operator=(const morkCoil& other);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKBLOB_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkBuilder.cpp
@@ -0,0 +1,1068 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+#ifndef _MORKBUILDER_
+#include "morkBuilder.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkBuilder::CloseMorkNode(morkEnv* ev) // CloseBuilder() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseBuilder(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkBuilder::~morkBuilder() // assert CloseBuilder() executed earlier
+{
+  MORK_ASSERT(mBuilder_Store==0);
+  MORK_ASSERT(mBuilder_Row==0);
+  MORK_ASSERT(mBuilder_Table==0);
+  MORK_ASSERT(mBuilder_Cell==0);
+  MORK_ASSERT(mBuilder_RowSpace==0);
+  MORK_ASSERT(mBuilder_AtomSpace==0);
+}
+
+/*public non-poly*/
+morkBuilder::morkBuilder(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+  morkStream* ioStream, mdb_count inBytesPerParseSegment,
+  nsIMdbHeap* ioSlotHeap, morkStore* ioStore)
+
+: morkParser(ev, inUsage, ioHeap, ioStream,
+  inBytesPerParseSegment, ioSlotHeap)
+  
+, mBuilder_Store( 0 )
+  
+, mBuilder_Table( 0 )
+, mBuilder_Row( 0 )
+, mBuilder_Cell( 0 )
+  
+, mBuilder_RowSpace( 0 )
+, mBuilder_AtomSpace( 0 )
+  
+, mBuilder_OidAtomSpace( 0 )
+, mBuilder_ScopeAtomSpace( 0 )
+  
+, mBuilder_PortForm( 0 )
+, mBuilder_PortRowScope( (mork_scope) 'r' )
+, mBuilder_PortAtomScope( (mork_scope) 'v' )
+
+, mBuilder_TableForm( 0 )
+, mBuilder_TableRowScope( (mork_scope) 'r' )
+, mBuilder_TableAtomScope( (mork_scope) 'v' )
+, mBuilder_TableKind( 0 )
+
+, mBuilder_TablePriority( morkPriority_kLo )
+, mBuilder_TableIsUnique( morkBool_kFalse )
+, mBuilder_TableIsVerbose( morkBool_kFalse )
+, mBuilder_TablePadByte( 0 )
+  
+, mBuilder_RowForm( 0 )
+, mBuilder_RowRowScope( (mork_scope) 'r' )
+, mBuilder_RowAtomScope( (mork_scope) 'v' )
+
+, mBuilder_CellForm( 0 )
+, mBuilder_CellAtomScope( (mork_scope) 'v' )
+
+, mBuilder_DictForm( 0 )
+, mBuilder_DictAtomScope( (mork_scope) 'v' )
+
+, mBuilder_MetaTokenSlot( 0 )
+  
+, mBuilder_DoCutRow( morkBool_kFalse )
+, mBuilder_DoCutCell( morkBool_kFalse )
+, mBuilder_CellsVecFill( 0 )
+{
+  if ( ev->Good() )
+  {
+    if ( ioStore )
+    {
+      morkStore::SlotWeakStore(ioStore, ev, &mBuilder_Store);
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kBuilder;
+    }
+    else
+      ev->NilPointerError();
+  }
+   
+}
+
+/*public non-poly*/ void
+morkBuilder::CloseBuilder(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mBuilder_Row = 0;
+      mBuilder_Cell = 0;
+      mBuilder_MetaTokenSlot = 0;
+      
+      morkTable::SlotStrongTable((morkTable*) 0, ev, &mBuilder_Table);
+      morkStore::SlotWeakStore((morkStore*) 0, ev, &mBuilder_Store);
+
+      morkRowSpace::SlotStrongRowSpace((morkRowSpace*) 0, ev,
+        &mBuilder_RowSpace);
+
+      morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+        &mBuilder_AtomSpace);
+
+      morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+        &mBuilder_OidAtomSpace);
+
+      morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+        &mBuilder_ScopeAtomSpace);
+      this->CloseParser(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkBuilder::NonBuilderTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkBuilder");
+}
+
+/*static*/ void
+morkBuilder::NilBuilderCellError(morkEnv* ev)
+{
+  ev->NewError("nil mBuilder_Cell");
+}
+
+/*static*/ void
+morkBuilder::NilBuilderRowError(morkEnv* ev)
+{
+  ev->NewError("nil mBuilder_Row");
+}
+
+/*static*/ void
+morkBuilder::NilBuilderTableError(morkEnv* ev)
+{
+  ev->NewError("nil mBuilder_Table");
+}
+
+/*static*/ void
+morkBuilder::NonColumnSpaceScopeError(morkEnv* ev)
+{
+  ev->NewError("column space != 'c'");
+}
+
+void
+morkBuilder::LogGlitch(morkEnv* ev, const morkGlitch& inGlitch, 
+  const char* inKind)
+{
+  MORK_USED_2(inGlitch,inKind);
+  ev->NewWarning("parsing glitch");
+}
+
+/*virtual*/ void
+morkBuilder::MidToYarn(morkEnv* ev,
+  const morkMid& inMid,  // typically an alias to concat with strings
+  mdbYarn* outYarn)
+// The parser might ask that some aliases be turned into yarns, so they
+// can be concatenated into longer blobs under some circumstances.  This
+// is an alternative to using a long and complex callback for many parts
+// for a single cell value.
+{
+  mBuilder_Store->MidToYarn(ev, inMid, outYarn);
+}
+
+/*virtual*/ void
+morkBuilder::OnNewPort(morkEnv* ev, const morkPlace& inPlace)
+// mp:Start     ::= OnNewPort mp:PortItem* OnPortEnd
+// mp:PortItem  ::= mp:Content | mp:Group | OnPortGlitch
+// mp:Content   ::= mp:PortRow | mp:Dict | mp:Table | mp:Row
+{
+  MORK_USED_2(ev,inPlace);
+  // mParser_InPort = morkBool_kTrue;
+  mBuilder_PortForm = 0;
+  mBuilder_PortRowScope = (mork_scope) 'r';
+  mBuilder_PortAtomScope = (mork_scope) 'v';
+}
+
+/*virtual*/ void
+morkBuilder::OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch)  
+{
+  this->LogGlitch(ev, inGlitch, "port");
+}
+
+/*virtual*/ void
+morkBuilder::OnPortEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Start     ::= OnNewPort mp:PortItem* OnPortEnd
+{
+  MORK_USED_2(ev,inSpan);
+  // ev->StubMethodOnlyError();
+  // nothing to do?
+  // mParser_InPort = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid)
+{
+  MORK_USED_1(inPlace);
+  mParser_InGroup = morkBool_kTrue;
+  mork_pos startPos = inPlace.mPlace_Pos;
+
+  morkStore* store = mBuilder_Store;
+  if ( store )
+  {
+    if ( inGid >= store->mStore_CommitGroupIdentity )
+      store->mStore_CommitGroupIdentity = inGid + 1;
+  
+    if ( !store->mStore_FirstCommitGroupPos )
+      store->mStore_FirstCommitGroupPos = startPos;
+    else if ( !store->mStore_SecondCommitGroupPos )
+      store->mStore_SecondCommitGroupPos = startPos;
+  }
+}
+
+/*virtual*/ void
+morkBuilder::OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) 
+{
+  this->LogGlitch(ev, inGlitch, "group");
+}
+
+/*virtual*/ void
+morkBuilder::OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan)  
+{
+  MORK_USED_2(ev,inSpan);
+  // mParser_InGroup = morkBool_kFalse;
+  // ev->StubMethodOnlyError();
+}
+
+/*virtual*/ void
+morkBuilder::OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) 
+{
+  MORK_USED_1(inSpan);
+  // mParser_InGroup = morkBool_kFalse;
+  ev->StubMethodOnlyError();
+}
+
+/*virtual*/ void
+morkBuilder::OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, 
+  const morkMid& inMid, mork_change inChange)
+{
+  MORK_USED_3(inMid,inPlace,inChange);
+  // mParser_InPortRow = morkBool_kTrue;
+  ev->StubMethodOnlyError();
+}
+
+/*virtual*/ void
+morkBuilder::OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+  this->LogGlitch(ev, inGlitch, "port row");
+}
+
+/*virtual*/ void
+morkBuilder::OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan)
+{
+  MORK_USED_1(inSpan);
+  // mParser_InPortRow = morkBool_kFalse;
+  ev->StubMethodOnlyError();
+}
+
+/*virtual*/ void
+morkBuilder::OnNewTable(morkEnv* ev, const morkPlace& inPlace,
+  const morkMid& inMid, mork_bool inCutAllRows)
+// mp:Table     ::= OnNewTable mp:TableItem* OnTableEnd
+// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch
+// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd
+// mp:Meta      ::= OnNewMeta mp:MetaItem* OnMetaEnd
+// mp:MetaItem  ::= mp:Cell | OnMetaGlitch
+{
+  MORK_USED_1(inPlace);
+  // mParser_InTable = morkBool_kTrue;
+  mBuilder_TableForm = mBuilder_PortForm;
+  mBuilder_TableRowScope = mBuilder_PortRowScope;
+  mBuilder_TableAtomScope = mBuilder_PortAtomScope;
+  mBuilder_TableKind = morkStore_kNoneToken;
+  
+  mBuilder_TablePriority = morkPriority_kLo;
+  mBuilder_TableIsUnique = morkBool_kFalse;
+  mBuilder_TableIsVerbose = morkBool_kFalse;
+
+  morkTable* table = mBuilder_Store->MidToTable(ev, inMid);
+  morkTable::SlotStrongTable(table, ev, &mBuilder_Table);
+  if ( table )
+  {
+    if ( table->mTable_RowSpace )
+      mBuilder_TableRowScope = table->mTable_RowSpace->SpaceScope();
+      
+    if ( inCutAllRows )
+      table->CutAllRows(ev);
+  }
+}
+
+/*virtual*/ void
+morkBuilder::OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+  this->LogGlitch(ev, inGlitch, "table");
+}
+
+/*virtual*/ void
+morkBuilder::OnTableEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Table     ::= OnNewTable mp:TableItem* OnTableEnd
+{
+  MORK_USED_1(inSpan);
+  // mParser_InTable = morkBool_kFalse;
+  if ( mBuilder_Table )
+  {
+    mBuilder_Table->mTable_Priority = mBuilder_TablePriority;
+    
+    if ( mBuilder_TableIsUnique )
+      mBuilder_Table->SetTableUnique();
+
+    if ( mBuilder_TableIsVerbose )
+      mBuilder_Table->SetTableVerbose();
+  
+    morkTable::SlotStrongTable((morkTable*) 0, ev, &mBuilder_Table);
+  }
+  else
+    this->NilBuilderTableError(ev);
+    
+  mBuilder_Row = 0;
+  mBuilder_Cell = 0;
+  
+  
+  mBuilder_TablePriority = morkPriority_kLo;
+  mBuilder_TableIsUnique = morkBool_kFalse;
+  mBuilder_TableIsVerbose = morkBool_kFalse;
+
+  if ( mBuilder_TableKind == morkStore_kNoneToken )
+    ev->NewError("missing table kind");
+
+  mBuilder_CellAtomScope = mBuilder_RowAtomScope =
+    mBuilder_TableAtomScope = mBuilder_PortAtomScope;
+
+  mBuilder_DoCutCell = morkBool_kFalse;
+  mBuilder_DoCutRow = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewMeta(morkEnv* ev, const morkPlace& inPlace)
+// mp:Meta      ::= OnNewMeta mp:MetaItem* OnMetaEnd
+// mp:MetaItem  ::= mp:Cell | OnMetaGlitch
+// mp:Cell      ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+// mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+  MORK_USED_2(ev,inPlace);
+  // mParser_InMeta = morkBool_kTrue;
+  
+}
+
+/*virtual*/ void
+morkBuilder::OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+  this->LogGlitch(ev, inGlitch, "meta");
+}
+
+/*virtual*/ void
+morkBuilder::OnMetaEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Meta      ::= OnNewMeta mp:MetaItem* OnMetaEnd
+{
+  MORK_USED_2(ev,inSpan);
+  // mParser_InMeta = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnMinusRow(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+  mBuilder_DoCutRow = morkBool_kTrue;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewRow(morkEnv* ev, const morkPlace& inPlace, 
+  const morkMid& inMid, mork_bool inCutAllCols)
+// mp:Table     ::= OnNewTable mp:TableItem* OnTableEnd
+// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch
+// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd
+// mp:Row       ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd
+// mp:RowItem   ::= mp:Cell | mp:Meta | OnRowGlitch
+// mp:Cell      ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+// mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+  MORK_USED_1(inPlace);
+  // mParser_InRow = morkBool_kTrue;
+  
+  mBuilder_CellForm = mBuilder_RowForm = mBuilder_TableForm;
+  mBuilder_CellAtomScope = mBuilder_RowAtomScope = mBuilder_TableAtomScope;
+  mBuilder_RowRowScope = mBuilder_TableRowScope;
+  morkStore* store = mBuilder_Store;
+  
+  if ( !inMid.mMid_Buf && !inMid.mMid_Oid.mOid_Scope )
+  {
+    morkMid mid(inMid);
+    mid.mMid_Oid.mOid_Scope = mBuilder_RowRowScope;
+    mBuilder_Row = store->MidToRow(ev, mid);
+  }
+  else
+  {
+    mBuilder_Row = store->MidToRow(ev, inMid);
+  }
+  morkRow* row = mBuilder_Row;
+  if ( row && inCutAllCols )
+  {
+    row->CutAllColumns(ev);
+  }
+
+  morkTable* table = mBuilder_Table;
+  if ( table )
+  {
+    if ( row )
+    {
+      if ( mParser_InMeta )
+      {
+        morkRow* metaRow = table->mTable_MetaRow;
+        if ( !metaRow )
+        {
+          table->mTable_MetaRow = row;
+          table->mTable_MetaRowOid = row->mRow_Oid;
+          row->AddRowGcUse(ev);
+        }
+        else if ( metaRow != row ) // not identical?
+          ev->NewError("duplicate table meta row");
+      }
+      else
+      {
+        if ( mBuilder_DoCutRow )
+          table->CutRow(ev, row);
+        else
+          table->AddRow(ev, row);
+      }
+    }
+  }
+  // else // it is now okay to have rows outside a table:
+  //  this->NilBuilderTableError(ev);
+    
+  mBuilder_DoCutRow = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnRowPos(morkEnv* ev, mork_pos inRowPos) 
+{
+  if ( mBuilder_Row && mBuilder_Table && !mParser_InMeta )
+  {
+    mork_pos hintFromPos = 0; // best hint when we don't know position
+    mBuilder_Table->MoveRow(ev, mBuilder_Row, hintFromPos, inRowPos);
+  }
+}
+
+/*virtual*/ void
+morkBuilder::OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) 
+{
+  this->LogGlitch(ev, inGlitch, "row");
+}
+
+void
+morkBuilder::FlushBuilderCells(morkEnv* ev)
+{
+  if ( mBuilder_Row )
+  {
+    morkPool* pool = mBuilder_Store->StorePool();
+    morkCell* cells = mBuilder_CellsVec;
+    mork_fill fill = mBuilder_CellsVecFill;
+    mBuilder_Row->TakeCells(ev, cells, fill, mBuilder_Store);
+
+    morkCell* end = cells + fill;
+    --cells; // prepare for preincrement
+    while ( ++cells < end )
+    {
+      if ( cells->mCell_Atom )
+        cells->SetAtom(ev, (morkAtom*) 0, pool);
+    }
+    mBuilder_CellsVecFill = 0;
+  }
+  else
+    this->NilBuilderRowError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnRowEnd(morkEnv* ev, const morkSpan& inSpan) 
+// mp:Row       ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd
+{
+  MORK_USED_1(inSpan);
+  // mParser_InRow = morkBool_kFalse;
+  if ( mBuilder_Row )
+  {
+    this->FlushBuilderCells(ev);
+  }
+  else
+    this->NilBuilderRowError(ev);
+    
+  mBuilder_Row = 0;
+  mBuilder_Cell = 0;
+
+  mBuilder_DoCutCell = morkBool_kFalse;
+  mBuilder_DoCutRow = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewDict(morkEnv* ev, const morkPlace& inPlace)
+// mp:Dict      ::= OnNewDict mp:DictItem* OnDictEnd
+// mp:DictItem  ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch
+{
+  MORK_USED_2(ev,inPlace);
+  // mParser_InDict = morkBool_kTrue;
+  
+  mBuilder_CellForm = mBuilder_DictForm = mBuilder_PortForm;
+  mBuilder_CellAtomScope = mBuilder_DictAtomScope = mBuilder_PortAtomScope;
+}
+
+/*virtual*/ void
+morkBuilder::OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) 
+{
+  this->LogGlitch(ev, inGlitch, "dict");
+}
+
+/*virtual*/ void
+morkBuilder::OnDictEnd(morkEnv* ev, const morkSpan& inSpan)  
+// mp:Dict      ::= OnNewDict mp:DictItem* OnDictEnd
+{
+  MORK_USED_2(ev,inSpan);
+  // mParser_InDict = morkBool_kFalse;
+
+  mBuilder_DictForm = 0;
+  mBuilder_DictAtomScope = 0;
+}
+
+/*virtual*/ void
+morkBuilder::OnAlias(morkEnv* ev, const morkSpan& inSpan,
+  const morkMid& inMid)
+{
+  MORK_USED_1(inSpan);
+  if ( mParser_InDict )
+  {
+    morkMid mid = inMid; // local copy for modification
+    mid.mMid_Oid.mOid_Scope = mBuilder_DictAtomScope;
+    mBuilder_Store->AddAlias(ev, mid, mBuilder_DictForm);
+  }
+  else
+    ev->NewError("alias not in dict");
+}
+
+/*virtual*/ void
+morkBuilder::OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+  this->LogGlitch(ev, inGlitch, "alias");
+}
+
+
+morkCell* 
+morkBuilder::AddBuilderCell(morkEnv* ev,
+  const morkMid& inMid, mork_change inChange)
+{
+  morkCell* outCell = 0;
+  mork_column column = inMid.mMid_Oid.mOid_Id;
+  
+  if ( ev->Good() )
+  {
+    if ( mBuilder_CellsVecFill >= morkBuilder_kCellsVecSize )
+      this->FlushBuilderCells(ev);
+    if ( ev->Good() )
+    {
+      if ( mBuilder_CellsVecFill < morkBuilder_kCellsVecSize )
+      {
+        mork_fill indx = mBuilder_CellsVecFill++;
+        outCell = mBuilder_CellsVec + indx;
+        outCell->SetColumnAndChange(column, inChange);
+        outCell->mCell_Atom = 0;
+      }
+      else
+        ev->NewError("out of builder cells");
+    }
+  }
+  return outCell;
+}
+
+/*virtual*/ void
+morkBuilder::OnMinusCell(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+  mBuilder_DoCutCell = morkBool_kTrue;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewCell(morkEnv* ev, const morkPlace& inPlace,
+    const morkMid* inMid, const morkBuf* inBuf)
+// Exactly one of inMid and inBuf is nil, and the other is non-nil.
+// When hex ID syntax is used for a column, then inMid is not nil, and
+// when a naked string names a column, then inBuf is not nil.
+  
+  // mp:Cell      ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+  // mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
+  // mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+  MORK_USED_1(inPlace);
+  // mParser_InCell = morkBool_kTrue;
+  
+  mork_change cellChange = ( mBuilder_DoCutCell )?
+    morkChange_kCut : morkChange_kAdd;
+    
+  mBuilder_DoCutCell = morkBool_kFalse;
+  
+  mBuilder_CellAtomScope = mBuilder_RowAtomScope;
+  
+  mBuilder_Cell = 0; // nil until determined for a row
+  morkStore* store = mBuilder_Store;
+  mork_scope scope = morkStore_kColumnSpaceScope;
+  morkMid tempMid; // space for local and modifiable cell mid
+  morkMid* cellMid = &tempMid; // default to local if inMid==0
+  
+  if ( inMid ) // mid parameter is actually provided?
+  {
+    *cellMid = *inMid; // bitwise copy for modifiable local mid
+
+    if ( !cellMid->mMid_Oid.mOid_Scope ) 
+    {
+      if ( cellMid->mMid_Buf )
+      {
+        scope = store->BufToToken(ev, cellMid->mMid_Buf);
+        cellMid->mMid_Buf = 0; // don't do scope lookup again
+        ev->NewWarning("column mids need column scope");
+      }
+      cellMid->mMid_Oid.mOid_Scope = scope;
+    }
+  }
+  else if ( inBuf ) // buf points to naked column string name?
+  {
+    cellMid->ClearMid();
+    cellMid->mMid_Oid.mOid_Id = store->BufToToken(ev, inBuf);
+    cellMid->mMid_Oid.mOid_Scope = scope; // kColumnSpaceScope
+  }
+  else
+    ev->NilPointerError(); // either inMid or inBuf must be non-nil
+
+  mork_column column = cellMid->mMid_Oid.mOid_Id;
+  
+  if ( mBuilder_Row && ev->Good() ) // this cell must be inside a row
+  {
+      // mBuilder_Cell = this->AddBuilderCell(ev, *cellMid, cellChange);
+
+      if ( mBuilder_CellsVecFill >= morkBuilder_kCellsVecSize )
+        this->FlushBuilderCells(ev);
+      if ( ev->Good() )
+      {
+        if ( mBuilder_CellsVecFill < morkBuilder_kCellsVecSize )
+        {
+          mork_fill ix = mBuilder_CellsVecFill++;
+          morkCell* cell =  mBuilder_CellsVec + ix;
+          cell->SetColumnAndChange(column, cellChange);
+          
+          cell->mCell_Atom = 0;
+          mBuilder_Cell = cell;
+        }
+        else
+          ev->NewError("out of builder cells");
+      }
+  }
+
+  else if ( mParser_InMeta &&  ev->Good() ) // cell is in metainfo structure?
+  {
+    if ( scope == morkStore_kColumnSpaceScope )
+    {
+      if ( mParser_InTable ) // metainfo for table?
+      {
+        if ( column == morkStore_kKindColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_TableKind;
+        else if ( column == morkStore_kStatusColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_TableStatus;
+        else if ( column == morkStore_kRowScopeColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_TableRowScope;
+        else if ( column == morkStore_kAtomScopeColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_TableAtomScope;
+        else if ( column == morkStore_kFormColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_TableForm;
+      }
+      else if ( mParser_InDict ) // metainfo for dict?
+      {
+        if ( column == morkStore_kAtomScopeColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_DictAtomScope;
+        else if ( column == morkStore_kFormColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_DictForm;
+      }
+      else if ( mParser_InRow ) // metainfo for row?
+      {
+        if ( column == morkStore_kAtomScopeColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_RowAtomScope;
+        else if ( column == morkStore_kRowScopeColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_RowRowScope;
+        else if ( column == morkStore_kFormColumn )
+          mBuilder_MetaTokenSlot = &mBuilder_RowForm;
+      }
+    }
+    else
+      ev->NewWarning("expected column scope");
+  }
+}
+
+/*virtual*/ void
+morkBuilder::OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+  this->LogGlitch(ev, inGlitch, "cell");
+}
+
+/*virtual*/ void
+morkBuilder::OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat)
+{
+  morkCell* cell = mBuilder_Cell;
+  if ( cell )
+  {
+    mBuilder_CellForm = inCharsetFormat;
+  }
+  else
+    this->NilBuilderCellError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnCellEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Cell      ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+{
+  MORK_USED_2(ev,inSpan);
+  // mParser_InCell = morkBool_kFalse;
+  
+  mBuilder_MetaTokenSlot = 0;
+  mBuilder_CellAtomScope = mBuilder_RowAtomScope;
+}
+
+/*virtual*/ void
+morkBuilder::OnValue(morkEnv* ev, const morkSpan& inSpan,
+  const morkBuf& inBuf)
+// mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+  MORK_USED_1(inSpan);
+  morkStore* store = mBuilder_Store;
+  morkCell* cell = mBuilder_Cell;
+  if ( cell )
+  {
+    mdbYarn yarn;
+    yarn.mYarn_Buf = inBuf.mBuf_Body;
+    yarn.mYarn_Fill = yarn.mYarn_Size = inBuf.mBuf_Fill;
+    yarn.mYarn_More = 0;
+    yarn.mYarn_Form = mBuilder_CellForm;
+    yarn.mYarn_Grow = 0;
+    morkAtom* atom = store->YarnToAtom(ev, &yarn, PR_TRUE /* create */);
+    cell->SetAtom(ev, atom, store->StorePool());
+  }
+  else if ( mParser_InMeta )
+  {
+    mork_token* metaSlot = mBuilder_MetaTokenSlot;
+    if ( metaSlot )
+    {
+      if ( metaSlot == &mBuilder_TableStatus ) // table status?
+      {
+        if ( mParser_InTable && mBuilder_Table )
+        {
+          const char* body = (const char*) inBuf.mBuf_Body;
+          mork_fill bufFill = inBuf.mBuf_Fill;
+          if ( body && bufFill )
+          {
+            const char* bodyEnd = body + bufFill;
+            while ( body < bodyEnd )
+            {
+              int c = *body++;
+              switch ( c )
+              {
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                  mBuilder_TablePriority = (mork_priority) ( c - '0' );
+                  break;
+                
+                case 'u':
+                case 'U':
+                  mBuilder_TableIsUnique = morkBool_kTrue;
+                  break;
+                  
+                case 'v':
+                case 'V':
+                  mBuilder_TableIsVerbose = morkBool_kTrue;
+                  break;
+              }
+            }
+          }
+        }
+      }
+      else
+      {
+        mork_token token = store->BufToToken(ev, &inBuf);
+        if ( token )
+        {
+          *metaSlot = token;
+          if ( metaSlot == &mBuilder_TableKind ) // table kind?
+          {
+            if ( mParser_InTable && mBuilder_Table )
+              mBuilder_Table->mTable_Kind = token;
+          }
+        }
+      }
+    }
+  }
+  else
+    this->NilBuilderCellError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnValueMid(morkEnv* ev, const morkSpan& inSpan,
+  const morkMid& inMid)
+// mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+  MORK_USED_1(inSpan);
+  morkStore* store = mBuilder_Store;
+  morkCell* cell = mBuilder_Cell;
+
+  morkMid valMid; // local mid for modifications
+  mdbOid* valOid = &valMid.mMid_Oid; // ref to oid inside mid
+  *valOid = inMid.mMid_Oid; // bitwise copy inMid's oid
+  
+  if ( inMid.mMid_Buf )
+  {
+    if ( !valOid->mOid_Scope )
+      store->MidToOid(ev, inMid, valOid);
+  }
+  else if ( !valOid->mOid_Scope )
+    valOid->mOid_Scope = mBuilder_CellAtomScope;
+  
+  if ( cell )
+  {
+    morkBookAtom* atom = store->MidToAtom(ev, valMid);
+    if ( atom )
+      cell->SetAtom(ev, atom, store->StorePool());
+    else
+      ev->NewError("undefined cell value alias");
+  }
+  else if ( mParser_InMeta )
+  {
+    mork_token* metaSlot = mBuilder_MetaTokenSlot;
+    if ( metaSlot )
+    {
+      mork_scope valScope = valOid->mOid_Scope;
+      if ( !valScope || valScope == morkStore_kColumnSpaceScope )
+      {
+        if ( ev->Good() && valMid.HasSomeId() )
+        {
+          *metaSlot = valOid->mOid_Id;
+          if ( metaSlot == &mBuilder_TableKind ) // table kind?
+          {
+            if ( mParser_InTable && mBuilder_Table )
+            {
+              mBuilder_Table->mTable_Kind = valOid->mOid_Id;
+            }
+            else
+              ev->NewWarning("mBuilder_TableKind not in table");
+          }
+          else if ( metaSlot == &mBuilder_TableStatus ) // table status?
+          {
+            if ( mParser_InTable && mBuilder_Table )
+            {
+              // $$ what here??
+            }
+            else
+              ev->NewWarning("mBuilder_TableStatus not in table");
+          }
+        }
+      }
+      else
+        this->NonColumnSpaceScopeError(ev);
+    }
+  }
+  else
+    this->NilBuilderCellError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnRowMid(morkEnv* ev, const morkSpan& inSpan,
+  const morkMid& inMid)
+// mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+  MORK_USED_1(inSpan);
+  morkStore* store = mBuilder_Store;
+  morkCell* cell = mBuilder_Cell;
+  if ( cell )
+  {
+    mdbOid rowOid = inMid.mMid_Oid;
+    if ( inMid.mMid_Buf )
+    {
+      if ( !rowOid.mOid_Scope )
+        store->MidToOid(ev, inMid, &rowOid);
+    }
+    else if ( !rowOid.mOid_Scope )
+      rowOid.mOid_Scope = mBuilder_RowRowScope;
+    
+    if ( ev->Good() )
+     {
+       morkPool* pool = store->StorePool();
+       morkAtom* atom = pool->NewRowOidAtom(ev, rowOid, &store->mStore_Zone);
+       if ( atom )
+       {
+         cell->SetAtom(ev, atom, pool);
+         morkRow* row = store->OidToRow(ev, &rowOid);
+         if ( row ) // found or created such a row?
+           row->AddRowGcUse(ev);
+       }
+     }
+  }
+  else
+    this->NilBuilderCellError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnTableMid(morkEnv* ev, const morkSpan& inSpan,
+  const morkMid& inMid)
+// mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+  MORK_USED_1(inSpan);
+  morkStore* store = mBuilder_Store;
+  morkCell* cell = mBuilder_Cell;
+  if ( cell )
+  {
+    mdbOid tableOid = inMid.mMid_Oid;
+    if ( inMid.mMid_Buf )
+    {
+      if ( !tableOid.mOid_Scope )
+        store->MidToOid(ev, inMid, &tableOid);
+    }
+    else if ( !tableOid.mOid_Scope )
+      tableOid.mOid_Scope = mBuilder_RowRowScope;
+    
+    if ( ev->Good() )
+     {
+       morkPool* pool = store->StorePool();
+       morkAtom* atom = pool->NewTableOidAtom(ev, tableOid, &store->mStore_Zone);
+       if ( atom )
+       {
+         cell->SetAtom(ev, atom, pool);
+         morkTable* table = store->OidToTable(ev, &tableOid,
+           /*optionalMetaRowOid*/ (mdbOid*) 0);
+         if ( table ) // found or created such a table?
+           table->AddTableGcUse(ev);
+       }
+     }
+  }
+  else
+    this->NilBuilderCellError(ev);
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkBuilder.h
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKBUILDER_
+#define _MORKBUILDER_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+ 
+/*| kCellsVecSize: length of cell vector buffer inside morkBuilder
+|*/
+#define morkBuilder_kCellsVecSize 64
+
+#define morkBuilder_kDefaultBytesPerParseSegment 512 /* plausible to big */
+
+#define morkDerived_kBuilder     /*i*/ 0x4275 /* ascii 'Bu' */
+
+class morkBuilder /*d*/ : public morkParser {
+
+// public: // slots inherited from morkParser (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+
+  // nsIMdbHeap*      mParser_Heap;   // refcounted heap used for allocation
+  // morkStream*   mParser_Stream; // refcounted input stream
+    
+  // mork_u4       mParser_Tag; // must equal morkParser_kTag
+  // mork_count    mParser_MoreGranularity; // constructor inBytesPerParseSegment
+
+  // mork_u4       mParser_State; // state where parser should resume
+ 
+  // after finding ends of group transactions, we can re-seek the start:
+  // mork_pos      mParser_GroupContentStartPos; // start of this group
+    
+  // mdbOid        mParser_TableOid; // table oid if inside a table
+  // mdbOid        mParser_RowOid;   // row oid if inside a row
+  // mork_gid      mParser_GroupId; // group ID if inside a group
+    
+  // mork_bool     mParser_InPort;  // called OnNewPort but not OnPortEnd?
+  // mork_bool     mParser_InDict;  // called OnNewDict but not OnDictEnd?
+  // mork_bool     mParser_InCell;  // called OnNewCell but not OnCellEnd?
+  // mork_bool     mParser_InMeta;  // called OnNewMeta but not OnMetaEnd?
+    
+  // morkMid     mParser_Mid;   // current alias being parsed
+  // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below:
+    
+  // blob coils allocated in mParser_Heap
+  // morkCoil     mParser_ScopeCoil;   // place to accumulate ID scope blobs
+  // morkCoil     mParser_ValueCoil;   // place to accumulate value blobs
+  // morkCoil     mParser_ColumnCoil;  // place to accumulate column blobs
+  // morkCoil     mParser_StringCoil;  // place to accumulate string blobs
+    
+  // morkSpool    mParser_ScopeSpool;  // writes to mParser_ScopeCoil
+  // morkSpool    mParser_ValueSpool;  // writes to mParser_ValueCoil
+  // morkSpool    mParser_ColumnSpool; // writes to mParser_ColumnCoil
+  // morkSpool    mParser_StringSpool; // writes to mParser_StringCoil
+
+  // yarns allocated in mParser_Heap
+  // morkYarn      mParser_MidYarn;   // place to receive from MidToYarn()
+  
+  // span showing current ongoing file position status:
+  // morkSpan      mParser_PortSpan; // span of current db port file
+    
+  // various spans denoting nested subspaces inside the file's port span:
+  // morkSpan      mParser_GroupSpan; // span of current transaction group
+  // morkSpan      mParser_DictSpan;
+  // morkSpan      mParser_AliasSpan;
+  // morkSpan      mParser_MetaDictSpan;
+  // morkSpan      mParser_TableSpan;
+  // morkSpan      mParser_MetaTableSpan;
+  // morkSpan      mParser_RowSpan;
+  // morkSpan      mParser_MetaRowSpan;
+  // morkSpan      mParser_CellSpan;
+  // morkSpan      mParser_ColumnSpan;
+  // morkSpan      mParser_SlotSpan;
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+protected: // protected morkBuilder members
+  
+  // weak refs that do not prevent closure of referenced nodes:
+  morkStore*       mBuilder_Store; // weak ref to builder's store
+  
+  // strong refs that do indeed prevent closure of referenced nodes:
+  morkTable*       mBuilder_Table;    // current table being built (or nil)
+  morkRow*         mBuilder_Row;      // current row being built (or nil)
+  morkCell*        mBuilder_Cell;     // current cell within CellsVec (or nil)
+  
+  morkRowSpace*    mBuilder_RowSpace;  // space for mBuilder_CellRowScope
+  morkAtomSpace*   mBuilder_AtomSpace; // space for mBuilder_CellAtomScope
+  
+  morkAtomSpace*   mBuilder_OidAtomSpace;   // ground atom space for oids
+  morkAtomSpace*   mBuilder_ScopeAtomSpace; // ground atom space for scopes
+  
+  // scoped object ids for current objects under construction:
+  mdbOid           mBuilder_TableOid; // full oid for current table
+  mdbOid           mBuilder_RowOid;   // full oid for current row
+      
+  // tokens that become set as the result of meta cells in port rows:
+  mork_cscode      mBuilder_PortForm;       // default port charset format
+  mork_scope       mBuilder_PortRowScope;   // port row scope
+  mork_scope       mBuilder_PortAtomScope;  // port atom scope
+
+  // tokens that become set as the result of meta cells in meta tables:
+  mork_cscode      mBuilder_TableForm;       // default table charset format
+  mork_scope       mBuilder_TableRowScope;   // table row scope
+  mork_scope       mBuilder_TableAtomScope;  // table atom scope
+  mork_kind        mBuilder_TableKind;       // table kind
+  
+  mork_token       mBuilder_TableStatus;  // dummy: priority/unique/verbose
+  
+  mork_priority    mBuilder_TablePriority;   // table priority
+  mork_bool        mBuilder_TableIsUnique;   // table uniqueness
+  mork_bool        mBuilder_TableIsVerbose;  // table verboseness
+  mork_u1          mBuilder_TablePadByte;    // for u4 alignment
+  
+  // tokens that become set as the result of meta cells in meta rows:
+  mork_cscode      mBuilder_RowForm;       // default row charset format
+  mork_scope       mBuilder_RowRowScope;   // row scope per row metainfo
+  mork_scope       mBuilder_RowAtomScope;  // row atom scope
+
+  // meta tokens currently in force, driven by meta info slots above:
+  mork_cscode      mBuilder_CellForm;       // cell charset format
+  mork_scope       mBuilder_CellAtomScope;  // cell atom scope
+
+  mork_cscode      mBuilder_DictForm;       // dict charset format
+  mork_scope       mBuilder_DictAtomScope;  // dict atom scope
+
+  mork_token*      mBuilder_MetaTokenSlot; // pointer to some slot above
+  
+  // If any of these 'cut' bools are true, it means a minus was seen in the
+  // Mork source text to indicate removal of content from some container.
+  // (Note there is no corresponding 'add' bool, since add is the default.)
+  // CutRow implies the current row should be cut from the table.
+  // CutCell implies the current column should be cut from the row.
+  mork_bool        mBuilder_DoCutRow;    // row with kCut change
+  mork_bool        mBuilder_DoCutCell;   // cell with kCut change
+  mork_u1          mBuilder_row_pad;    // pad to u4 alignment
+  mork_u1          mBuilder_cell_pad;   // pad to u4 alignment
+  
+  morkCell         mBuilder_CellsVec[ morkBuilder_kCellsVecSize + 1 ];
+  mork_fill        mBuilder_CellsVecFill; // count used in CellsVec
+  // Note when mBuilder_CellsVecFill equals morkBuilder_kCellsVecSize, and 
+  // another cell is added, this means all the cells in the vector above
+  // must be flushed to the current row being built to create more room.
+  
+protected: // protected inlines
+
+  mork_bool  CellVectorIsFull() const
+  { return ( mBuilder_CellsVecFill == morkBuilder_kCellsVecSize ); }
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseBuilder() only if open
+  virtual ~morkBuilder(); // assert that CloseBuilder() executed earlier
+  
+public: // morkYarn construction & destruction
+  morkBuilder(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+    morkStream* ioStream,  // the readonly stream for input bytes
+    mdb_count inBytesPerParseSegment, // target for ParseMore()
+    nsIMdbHeap* ioSlotHeap, morkStore* ioStore
+    );
+      
+  void CloseBuilder(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkBuilder(const morkBuilder& other);
+  morkBuilder& operator=(const morkBuilder& other);
+
+public: // dynamic type identification
+  mork_bool IsBuilder() const
+  { return IsNode() && mNode_Derived == morkDerived_kBuilder; }
+// } ===== end morkNode methods =====
+
+public: // errors
+  static void NonBuilderTypeError(morkEnv* ev);
+  static void NilBuilderCellError(morkEnv* ev);
+  static void NilBuilderRowError(morkEnv* ev);
+  static void NilBuilderTableError(morkEnv* ev);
+  static void NonColumnSpaceScopeError(morkEnv* ev);
+  
+  void LogGlitch(morkEnv* ev, const morkGlitch& inGlitch, 
+    const char* inKind);
+
+public: // other builder methods
+
+  morkCell* AddBuilderCell(morkEnv* ev,
+    const morkMid& inMid, mork_change inChange);
+
+  void FlushBuilderCells(morkEnv* ev);
+  
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // in virtual morkParser methods, data flow subclass to parser
+
+    virtual void MidToYarn(morkEnv* ev,
+      const morkMid& inMid,  // typically an alias to concat with strings
+      mdbYarn* outYarn);
+    // The parser might ask that some aliases be turned into yarns, so they
+    // can be concatenated into longer blobs under some circumstances.  This
+    // is an alternative to using a long and complex callback for many parts
+    // for a single cell value.
+  
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // out virtual morkParser methods, data flow parser to subclass
+
+  virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace);
+  virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch);  
+  virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan);  
+
+  virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid);
+  virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch);  
+  virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan);  
+  virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan);  
+
+  virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, 
+    const morkMid& inMid, mork_change inChange);
+  virtual void OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch);  
+  virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan);  
+
+  virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace,
+    const morkMid& inMid, mork_bool inCutAllRows);
+  virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch);
+  virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan);
+    
+  virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace);
+  virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch);
+  virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan);
+
+  virtual void OnMinusRow(morkEnv* ev);
+  virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace, 
+    const morkMid& inMid, mork_bool inCutAllCols);
+  virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos);  
+  virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch);  
+  virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan);  
+
+  virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace);
+  virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch);  
+  virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan);  
+
+  virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan,
+    const morkMid& inMid);
+
+  virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch);
+
+  virtual void OnMinusCell(morkEnv* ev);
+  virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace,
+    const morkMid* inMid, const morkBuf* inBuf);
+  // Exactly one of inMid and inBuf is nil, and the other is non-nil.
+  // When hex ID syntax is used for a column, then inMid is not nil, and
+  // when a naked string names a column, then inBuf is not nil.
+
+  virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch);
+  virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat);
+  virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan);
+    
+  virtual void OnValue(morkEnv* ev, const morkSpan& inSpan,
+    const morkBuf& inBuf);
+
+  virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan,
+    const morkMid& inMid);
+
+  virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan,
+    const morkMid& inMid);
+
+  virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan,
+    const morkMid& inMid);
+  
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public non-poly morkBuilder methods
+  
+  
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakBuilder(morkBuilder* me,
+    morkEnv* ev, morkBuilder** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongBuilder(morkBuilder* me,
+    morkEnv* ev, morkBuilder** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKBUILDER_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkCell.cpp
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+void
+morkCell::SetYarn(morkEnv* ev, const mdbYarn* inYarn, morkStore* ioStore)
+{
+  morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, PR_TRUE /* create */);
+  if ( atom )
+    this->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom
+}
+
+void
+morkCell::GetYarn(morkEnv* ev, mdbYarn* outYarn) const
+{
+  MORK_USED_1(ev);
+  mCell_Atom->GetYarn(outYarn);
+}
+
+void
+morkCell::AliasYarn(morkEnv* ev, mdbYarn* outYarn) const
+{
+  MORK_USED_1(ev);
+  mCell_Atom->AliasYarn(outYarn);
+}
+  
+  
+void
+morkCell::SetCellClean()
+{
+  mork_column col = this->GetColumn();
+  this->SetColumnAndChange(col, morkChange_kNil);
+}
+  
+void
+morkCell::SetCellDirty()
+{
+  mork_column col = this->GetColumn();
+  this->SetColumnAndChange(col, morkChange_kAdd);
+}
+
+void
+morkCell::SetAtom(morkEnv* ev, morkAtom* ioAtom, morkPool* ioPool)
+  // SetAtom() "acquires" the new ioAtom if non-nil, by calling AddCellUse()
+  // to increase the refcount, and puts ioAtom into mCell_Atom.  If the old
+  // atom in mCell_Atom is non-nil, then it is "released" first by a call to
+  // CutCellUse(), and if the use count then becomes zero, then the old atom
+  // is deallocated by returning it to the pool ioPool.  (And this is
+  // why ioPool is a parameter to this method.)  Note that ioAtom can be nil
+  // to cause the cell to refer to nothing, and the old atom in mCell_Atom
+  // can also be nil, and all the atom refcounting is handled correctly.
+  //
+  // Note that if ioAtom was just created, it typically has a zero use count
+  // before calling SetAtom().  But use count is one higher after SetAtom().
+{
+  morkAtom* oldAtom = mCell_Atom;
+  if ( oldAtom != ioAtom ) // ioAtom is not already installed in this cell?
+  {
+    if ( oldAtom )
+    {
+      mCell_Atom = 0;
+      if ( oldAtom->CutCellUse(ev) == 0 )
+      {
+      // this was zapping atoms still in use - comment out until davidmc
+      // can figure out a better fix.
+//        if ( ioPool )
+//        {
+//          if ( oldAtom->IsBook() )
+//            ((morkBookAtom*) oldAtom)->CutBookAtomFromSpace(ev);
+            
+//          ioPool->ZapAtom(ev, oldAtom);
+//        }
+//        else
+//          ev->NilPointerError();
+      }
+    }
+    if ( ioAtom )
+      ioAtom->AddCellUse(ev);
+      
+    mCell_Atom = ioAtom;
+  }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkCell.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKCELL_
+#define _MORKCELL_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDelta_kShift 8 /* 8 bit shift */
+#define morkDelta_kChangeMask 0x0FF /* low 8 bit mask */
+#define morkDelta_kColumnMask (~ (mork_column) morkDelta_kChangeMask)
+#define morkDelta_Init(self,cl,ch) ((self) = ((cl)<<morkDelta_kShift) | (ch))
+#define morkDelta_Change(self) ((mork_change) ((self) & morkDelta_kChangeMask))
+#define morkDelta_Column(self) ((self) >> morkDelta_kShift)
+
+class morkCell { // minimal cell format
+
+public:
+  mork_delta   mCell_Delta;   // encoding of both column and change
+  morkAtom*    mCell_Atom;    // content in this cell
+  
+public:
+  morkCell() : mCell_Delta( 0 ), mCell_Atom( 0 ) { }
+
+  morkCell(const morkCell& c)
+  : mCell_Delta( c.mCell_Delta ), mCell_Atom( c.mCell_Atom ) { }
+  
+  // note if ioAtom is non-nil, caller needs to call ioAtom->AddCellUse():
+  morkCell(mork_column inCol, mork_change inChange, morkAtom* ioAtom)
+  {
+    morkDelta_Init(mCell_Delta, inCol,inChange);
+    mCell_Atom = ioAtom;
+  }
+
+  // note if ioAtom is non-nil, caller needs to call ioAtom->AddCellUse():
+  void Init(mork_column inCol, mork_change inChange, morkAtom* ioAtom)
+  {
+    morkDelta_Init(mCell_Delta,inCol,inChange);
+    mCell_Atom = ioAtom;
+  }
+  
+  mork_column  GetColumn() const { return morkDelta_Column(mCell_Delta); }
+  mork_change  GetChange() const { return morkDelta_Change(mCell_Delta); }
+  
+  mork_bool IsCellClean() const { return GetChange() == morkChange_kNil; }
+  mork_bool IsCellDirty() const { return GetChange() != morkChange_kNil; }
+
+  void SetCellClean(); // set change to kNil
+  void SetCellDirty(); // set change to kAdd
+  
+  void SetCellColumnDirty(mork_column inCol)
+  { this->SetColumnAndChange(inCol, morkChange_kAdd); }
+  
+  void SetCellColumnClean(mork_column inCol)
+  { this->SetColumnAndChange(inCol, morkChange_kNil); }
+  
+  void         SetColumnAndChange(mork_column inCol, mork_change inChange)
+  { morkDelta_Init(mCell_Delta, inCol, inChange); }
+    
+  morkAtom*  GetAtom() { return mCell_Atom; }
+  
+  void       SetAtom(morkEnv* ev, morkAtom* ioAtom, morkPool* ioPool);
+  // SetAtom() "acquires" the new ioAtom if non-nil, by calling AddCellUse()
+  // to increase the refcount, and puts ioAtom into mCell_Atom.  If the old
+  // atom in mCell_Atom is non-nil, then it is "released" first by a call to
+  // CutCellUse(), and if the use count then becomes zero, then the old atom
+  // is deallocated by returning it to the pool ioPool.  (And this is
+  // why ioPool is a parameter to this method.)  Note that ioAtom can be nil
+  // to cause the cell to refer to nothing, and the old atom in mCell_Atom
+  // can also be nil, and all the atom refcounting is handled correctly.
+  //
+  // Note that if ioAtom was just created, it typically has a zero use count
+  // before calling SetAtom().  But use count is one higher after SetAtom().
+  
+  void       SetYarn(morkEnv* ev, const mdbYarn* inYarn, morkStore* ioStore);
+  
+  void       AliasYarn(morkEnv* ev, mdbYarn* outYarn) const;
+  void       GetYarn(morkEnv* ev, mdbYarn* outYarn) const;
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKCELL_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkCellObject.cpp
@@ -0,0 +1,567 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCELLOBJECT_
+#include "morkCellObject.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkCellObject::CloseMorkNode(morkEnv* ev) // CloseCellObject() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseCellObject(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkCellObject::~morkCellObject() // assert CloseCellObject() executed earlier
+{
+  CloseMorkNode(mMorkEnv);
+  MORK_ASSERT(mCellObject_Row==0);
+}
+
+/*public non-poly*/
+morkCellObject::morkCellObject(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, morkRow* ioRow, morkCell* ioCell,
+  mork_column inCol, mork_pos inPos)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mCellObject_RowObject( 0 )
+, mCellObject_Row( 0 )
+, mCellObject_Cell( 0 )
+, mCellObject_Col( inCol )
+, mCellObject_RowSeed( 0 )
+, mCellObject_Pos( (mork_u2) inPos )
+{
+  if ( ev->Good() )
+  {
+    if ( ioRow && ioCell )
+    {
+      if ( ioRow->IsRow() )
+      {
+        morkStore* store = ioRow->GetRowSpaceStore(ev);
+        if ( store )
+        {
+          morkRowObject* rowObj = ioRow->AcquireRowObject(ev, store);
+          if ( rowObj )
+          {
+            mCellObject_Row = ioRow;
+            mCellObject_Cell = ioCell;
+            mCellObject_RowSeed = ioRow->mRow_Seed;
+            
+            // morkRowObject::SlotStrongRowObject(rowObj, ev,
+            //  &mCellObject_RowObject);
+              
+            mCellObject_RowObject = rowObj; // assume control of strong ref
+          }
+          if ( ev->Good() )
+            mNode_Derived = morkDerived_kCellObject;
+        }
+      }
+      else
+        ioRow->NonRowTypeError(ev);
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkCellObject, morkObject, nsIMdbCell)
+
+/*public non-poly*/ void
+morkCellObject::CloseCellObject(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      NS_RELEASE(mCellObject_RowObject);
+      mCellObject_Row = 0;
+      mCellObject_Cell = 0;
+      mCellObject_RowSeed = 0;
+      this->CloseObject(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+mork_bool
+morkCellObject::ResyncWithRow(morkEnv* ev)
+{
+  morkRow* row = mCellObject_Row;
+  mork_pos pos = 0;
+  morkCell* cell = row->GetCell(ev, mCellObject_Col, &pos);
+  if ( cell )
+  {
+    mCellObject_Pos = (mork_u2) pos;
+    mCellObject_Cell = cell;
+    mCellObject_RowSeed = row->mRow_Seed;
+  }
+  else
+  {
+    mCellObject_Cell = 0;
+    this->MissingRowColumnError(ev);
+  }
+  return ev->Good();
+}
+
+morkAtom*
+morkCellObject::GetCellAtom(morkEnv* ev) const
+{
+  morkCell* cell = mCellObject_Cell;
+  if ( cell )
+    return cell->GetAtom();
+  else
+    this->NilCellError(ev);
+    
+  return (morkAtom*) 0;
+}
+
+/*static*/ void
+morkCellObject::WrongRowObjectRowError(morkEnv* ev)
+{
+  ev->NewError("mCellObject_Row != mCellObject_RowObject->mRowObject_Row");
+}
+
+/*static*/ void
+morkCellObject::NilRowError(morkEnv* ev)
+{
+  ev->NewError("nil mCellObject_Row");
+}
+
+/*static*/ void
+morkCellObject::NilRowObjectError(morkEnv* ev)
+{
+  ev->NewError("nil mCellObject_RowObject");
+}
+
+/*static*/ void
+morkCellObject::NilCellError(morkEnv* ev)
+{
+  ev->NewError("nil mCellObject_Cell");
+}
+
+/*static*/ void
+morkCellObject::NonCellObjectTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkCellObject");
+}
+
+/*static*/ void
+morkCellObject::MissingRowColumnError(morkEnv* ev)
+{
+  ev->NewError("mCellObject_Col not in mCellObject_Row");
+}
+
+nsIMdbCell*
+morkCellObject::AcquireCellHandle(morkEnv* ev)
+{
+  nsIMdbCell* outCell = this;
+  NS_ADDREF(outCell);
+  return outCell;
+}
+
+
+morkEnv*
+morkCellObject::CanUseCell(nsIMdbEnv* mev, mork_bool inMutable,
+  mdb_err* outErr, morkCell** outCell) 
+{
+  morkEnv* outEnv = 0;
+  morkCell* cell = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( IsCellObject() )
+    {
+      if ( IsMutable() || !inMutable )
+      {
+        morkRowObject* rowObj = mCellObject_RowObject;
+        if ( rowObj )
+        {
+          morkRow* row = mCellObject_Row;
+          if ( row )
+          {
+            if ( rowObj->mRowObject_Row == row )
+            {
+              mork_u2 oldSeed = mCellObject_RowSeed;
+              if ( row->mRow_Seed == oldSeed || ResyncWithRow(ev) )
+              {
+                cell = mCellObject_Cell;
+                if ( cell )
+                {
+                  outEnv = ev;
+                }
+                else
+                  NilCellError(ev);
+              }
+            }
+            else
+              WrongRowObjectRowError(ev);
+          }
+          else
+            NilRowError(ev);
+        }
+        else
+          NilRowObjectError(ev);
+      }
+      else
+        NonMutableNodeError(ev);
+    }
+    else
+      NonCellObjectTypeError(ev);
+  }
+  *outErr = ev->AsErr();
+  MORK_ASSERT(outEnv);
+  *outCell = cell;
+  
+  return outEnv;
+}
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP morkCellObject::SetBlob(nsIMdbEnv* /* mev */,
+  nsIMdbBlob* /* ioBlob */)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+} // reads inBlob slots
+
+// when inBlob is in the same suite, this might be fastest cell-to-cell
+
+NS_IMETHODIMP morkCellObject::ClearBlob( // make empty (so content has zero length)
+  nsIMdbEnv*  /* mev */)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+  // remember row->MaybeDirtySpaceStoreAndRow();
+}
+// clearing a yarn is like SetYarn() with empty yarn instance content
+
+NS_IMETHODIMP morkCellObject::GetBlobFill(nsIMdbEnv* mev,
+  mdb_fill* outFill)
+// Same value that would be put into mYarn_Fill, if one called GetYarn()
+// with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0.
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}  // size of blob 
+
+NS_IMETHODIMP morkCellObject::SetYarn(nsIMdbEnv* mev, 
+  const mdbYarn* inYarn)
+{
+  mdb_err outErr = 0;
+  morkCell* cell = 0;
+  morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+    &outErr, &cell);
+  if ( ev )
+  {
+    morkRow* row = mCellObject_Row;
+    if ( row )
+    {
+      morkStore* store = row->GetRowSpaceStore(ev);
+      if ( store )
+      {
+        cell->SetYarn(ev, inYarn, store);
+        if ( row->IsRowClean() && store->mStore_CanDirty )
+          row->MaybeDirtySpaceStoreAndRow();
+      }
+    }
+    else
+      ev->NilPointerError();
+
+    outErr = ev->AsErr();
+  }
+    
+  return outErr;
+}   // reads from yarn slots
+// make this text object contain content from the yarn's buffer
+
+NS_IMETHODIMP morkCellObject::GetYarn(nsIMdbEnv* mev, 
+  mdbYarn* outYarn)
+{
+  mdb_err outErr = 0;
+  morkCell* cell = 0;
+  morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+    &outErr, &cell);
+  if ( ev )
+  {
+    morkAtom* atom = cell->GetAtom();
+    atom->GetYarn(outYarn);
+    outErr = ev->AsErr();
+  }
+    
+  return outErr;
+}  // writes some yarn slots 
+// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+
+NS_IMETHODIMP morkCellObject::AliasYarn(nsIMdbEnv* mev, 
+  mdbYarn* outYarn)
+{
+  mdb_err outErr = 0;
+  morkCell* cell = 0;
+  morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+    &outErr, &cell);
+  if ( ev )
+  {
+    morkAtom* atom = cell->GetAtom();
+    atom->AliasYarn(outYarn);
+    outErr = ev->AsErr();
+  }
+    
+  return outErr;
+} // writes ALL yarn slots
+
+// } ----- end attribute methods -----
+
+// } ===== end nsIMdbBlob methods =====
+
+// { ===== begin nsIMdbCell methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP morkCellObject::SetColumn(nsIMdbEnv* mev, mdb_column inColumn)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+  // remember row->MaybeDirtySpaceStoreAndRow();
+} 
+
+NS_IMETHODIMP morkCellObject::GetColumn(nsIMdbEnv* mev, mdb_column* outColumn)
+{
+  mdb_err outErr = 0;
+  mdb_column col = 0;
+  morkCell* cell = 0;
+  morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+    &outErr, &cell);
+  if ( ev )
+  {
+    col = mCellObject_Col;
+    outErr = ev->AsErr();
+  }
+  if ( outColumn )
+    *outColumn = col;
+  return outErr;
+}
+
+NS_IMETHODIMP morkCellObject::GetCellInfo(  // all cell metainfo except actual content
+  nsIMdbEnv* mev, 
+  mdb_column* outColumn,           // the column in the containing row
+  mdb_fill*   outBlobFill,         // the size of text content in bytes
+  mdbOid*     outChildOid,         // oid of possible row or table child
+  mdb_bool*   outIsRowChild)  // nonzero if child, and a row child
+// Checking all cell metainfo is a good way to avoid forcing a large cell
+// in to memory when you don't actually want to use the content.
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP morkCellObject::GetRow(nsIMdbEnv* mev, // parent row for this cell
+  nsIMdbRow** acqRow)
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkCell* cell = 0;
+  morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+    &outErr, &cell);
+  if ( ev )
+  {
+    outRow = mCellObject_RowObject->AcquireRowHandle(ev);
+    
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+
+NS_IMETHODIMP morkCellObject::GetPort(nsIMdbEnv* mev, // port containing cell
+  nsIMdbPort** acqPort)
+{
+  mdb_err outErr = 0;
+  nsIMdbPort* outPort = 0;
+  morkCell* cell = 0;
+  morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+    &outErr, &cell);
+  if ( ev )
+  {
+    if ( mCellObject_Row )
+    {
+      morkStore* store = mCellObject_Row->GetRowSpaceStore(ev);
+      if ( store )
+        outPort = store->AcquireStoreHandle(ev);
+    }
+    else
+      ev->NilPointerError();
+
+    outErr = ev->AsErr();
+  }
+  if ( acqPort )
+    *acqPort = outPort;
+  return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin children methods -----
+NS_IMETHODIMP morkCellObject::HasAnyChild( // does cell have a child instead of text?
+  nsIMdbEnv* mev,
+  mdbOid* outOid,  // out id of row or table (or unbound if no child)
+  mdb_bool* outIsRow) // nonzero if child is a row (rather than a table)
+{
+  mdb_err outErr = 0;
+  mdb_bool isRow = morkBool_kFalse;
+  outOid->mOid_Scope = 0;
+  outOid->mOid_Id = morkId_kMinusOne;
+  morkCell* cell = 0;
+  morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+    &outErr, &cell);
+  if ( ev )
+  {
+    morkAtom* atom = GetCellAtom(ev);
+    if ( atom )
+    {
+      isRow = atom->IsRowOid();
+      if ( isRow || atom->IsTableOid() )
+        *outOid = ((morkOidAtom*) atom)->mOidAtom_Oid;
+    }
+      
+    outErr = ev->AsErr();
+  }
+  if ( outIsRow )
+    *outIsRow = isRow;
+    
+  return outErr;
+}
+
+NS_IMETHODIMP morkCellObject::GetAnyChild( // access table of specific attribute
+  nsIMdbEnv* mev, // context
+  nsIMdbRow** acqRow, // child row (or null)
+  nsIMdbTable** acqTable) // child table (or null)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP morkCellObject::SetChildRow( // access table of specific attribute
+  nsIMdbEnv* mev, // context
+  nsIMdbRow* ioRow)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+} // inRow must be bound inside this same db port
+
+NS_IMETHODIMP morkCellObject::GetChildRow( // access row of specific attribute
+  nsIMdbEnv* mev, // context
+  nsIMdbRow** acqRow) // acquire child row (or nil if no child)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP morkCellObject::SetChildTable( // access table of specific attribute
+  nsIMdbEnv* mev, // context
+  nsIMdbTable* inTable) // table must be bound inside this same db port
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+  // remember row->MaybeDirtySpaceStoreAndRow();
+}
+
+NS_IMETHODIMP morkCellObject::GetChildTable( // access table of specific attribute
+  nsIMdbEnv* mev, // context
+  nsIMdbTable** acqTable) // acquire child tabdle (or nil if no chil)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end children methods -----
+
+// } ===== end nsIMdbCell methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkCellObject.h
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKCELLOBJECT_
+#define _MORKCELLOBJECT_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kCellObject  /*i*/ 0x634F /* ascii 'cO' */
+
+class morkCellObject : public morkObject, public nsIMdbCell { // blob attribute in column scope
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+  // mork_color   mBead_Color;   // ID for this bead
+  // morkHandle*  mObject_Handle;  // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+  NS_DECL_ISUPPORTS_INHERITED
+
+  morkRowObject*  mCellObject_RowObject;  // strong ref to row's object
+  morkRow*        mCellObject_Row;        // cell's row if still in row object
+  morkCell*       mCellObject_Cell;       // cell in row if rowseed matches
+  mork_column     mCellObject_Col;        // col of cell last living in pos
+  mork_u2         mCellObject_RowSeed;    // copy of row's seed
+  mork_u2         mCellObject_Pos;        // position of cell in row
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseCellObject() only if open
+  virtual ~morkCellObject(); // assert that CloseCellObject() executed earlier
+  
+public: // morkCellObject construction & destruction
+  morkCellObject(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkRow* ioRow, morkCell* ioCell,
+    mork_column inCol, mork_pos inPos);
+  void CloseCellObject(morkEnv* ev); // called by CloseMorkNode();
+
+  NS_IMETHOD SetBlob(nsIMdbEnv* ev,
+    nsIMdbBlob* ioBlob); // reads inBlob slots
+  // when inBlob is in the same suite, this might be fastest cell-to-cell
+  
+  NS_IMETHOD ClearBlob( // make empty (so content has zero length)
+    nsIMdbEnv* ev);
+  // clearing a yarn is like SetYarn() with empty yarn instance content
+  
+  NS_IMETHOD GetBlobFill(nsIMdbEnv* ev,
+    mdb_fill* outFill);  // size of blob 
+  // Same value that would be put into mYarn_Fill, if one called GetYarn()
+  // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0.
+  
+  NS_IMETHOD SetYarn(nsIMdbEnv* ev, 
+    const mdbYarn* inYarn);   // reads from yarn slots
+  // make this text object contain content from the yarn's buffer
+  
+  NS_IMETHOD GetYarn(nsIMdbEnv* ev, 
+    mdbYarn* outYarn);  // writes some yarn slots 
+  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+  
+  NS_IMETHOD AliasYarn(nsIMdbEnv* ev, 
+    mdbYarn* outYarn); // writes ALL yarn slots
+  NS_IMETHOD SetColumn(nsIMdbEnv* ev, mdb_column inColumn); 
+  NS_IMETHOD GetColumn(nsIMdbEnv* ev, mdb_column* outColumn);
+  
+  NS_IMETHOD GetCellInfo(  // all cell metainfo except actual content
+    nsIMdbEnv* ev, 
+    mdb_column* outColumn,           // the column in the containing row
+    mdb_fill*   outBlobFill,         // the size of text content in bytes
+    mdbOid*     outChildOid,         // oid of possible row or table child
+    mdb_bool*   outIsRowChild);  // nonzero if child, and a row child
+
+  // Checking all cell metainfo is a good way to avoid forcing a large cell
+  // in to memory when you don't actually want to use the content.
+  
+  NS_IMETHOD GetRow(nsIMdbEnv* ev, // parent row for this cell
+    nsIMdbRow** acqRow);
+  NS_IMETHOD GetPort(nsIMdbEnv* ev, // port containing cell
+    nsIMdbPort** acqPort);
+  // } ----- end attribute methods -----
+
+  // { ----- begin children methods -----
+  NS_IMETHOD HasAnyChild( // does cell have a child instead of text?
+    nsIMdbEnv* ev,
+    mdbOid* outOid,  // out id of row or table (or unbound if no child)
+    mdb_bool* outIsRow); // nonzero if child is a row (rather than a table)
+
+  NS_IMETHOD GetAnyChild( // access table of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbRow** acqRow, // child row (or null)
+    nsIMdbTable** acqTable); // child table (or null)
+
+
+  NS_IMETHOD SetChildRow( // access table of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow); // inRow must be bound inside this same db port
+
+  NS_IMETHOD GetChildRow( // access row of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbRow** acqRow); // acquire child row (or nil if no child)
+
+
+  NS_IMETHOD SetChildTable( // access table of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbTable* inTable); // table must be bound inside this same db port
+
+  NS_IMETHOD GetChildTable( // access table of specific attribute
+    nsIMdbEnv* ev, // context
+    nsIMdbTable** acqTable); // acquire child table (or nil if no child)
+  // } ----- end children methods -----
+
+// } ===== end nsIMdbCell methods =====
+private: // copying is not allowed
+  morkCellObject(const morkCellObject& other);
+  morkCellObject& operator=(const morkCellObject& other);
+
+public: // dynamic type identification
+  mork_bool IsCellObject() const
+  { return IsNode() && mNode_Derived == morkDerived_kCellObject; }
+// } ===== end morkNode methods =====
+
+public: // other cell node methods
+
+  morkEnv*  CanUseCell(nsIMdbEnv* mev, mork_bool inMutable,
+    mdb_err* outErr, morkCell** outCell) ;
+
+  mork_bool ResyncWithRow(morkEnv* ev); // return ev->Good()
+  morkAtom* GetCellAtom(morkEnv* ev) const;
+
+  static void MissingRowColumnError(morkEnv* ev);
+  static void NilRowError(morkEnv* ev);
+  static void NilCellError(morkEnv* ev);
+  static void NilRowObjectError(morkEnv* ev);
+  static void WrongRowObjectRowError(morkEnv* ev);
+  static void NonCellObjectTypeError(morkEnv* ev);
+
+  nsIMdbCell* AcquireCellHandle(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakCellObject(morkCellObject* me,
+    morkEnv* ev, morkCellObject** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongCellObject(morkCellObject* me,
+    morkEnv* ev, morkCellObject** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKCELLOBJECT_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkCh.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCH_
+#include "morkCh.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/* this byte char predicate source file derives from public domain Mithril */
+/* (that means much of this has a copyright dedicated to the public domain) */
+
+/*============================================================================*/
+/* morkCh_Type */
+
+const mork_flags morkCh_Type[] = /* derives from public domain Mithril table */
+{
+  0,                /* 0x0 */
+  0,                /* 0x1 */
+  0,                /* 0x2 */
+  0,                /* 0x3 */
+  0,                /* 0x4 */
+  0,                /* 0x5 */
+  0,                /* 0x6 */
+  0,                /* 0x7 */
+  morkCh_kW,        /* 0x8 backspace */
+  morkCh_kW,        /* 0x9 tab */
+  morkCh_kW,        /* 0xA linefeed */
+  0,                /* 0xB */
+  morkCh_kW,        /* 0xC page */
+  morkCh_kW,        /* 0xD return */
+  0,                /* 0xE */
+  0,                /* 0xF */
+  0,                /* 0x10 */
+  0,                /* 0x11 */
+  0,                /* 0x12 */
+  0,                /* 0x13 */
+  0,                /* 0x14 */
+  0,                /* 0x15 */
+  0,                /* 0x16 */
+  0,                /* 0x17 */
+  0,                /* 0x18 */
+  0,                /* 0x19 */
+  0,                /* 0x1A */
+  0,                /* 0x1B */
+  0,                /* 0x1C */
+  0,                /* 0x1D */
+  0,                /* 0x1E */
+  0,                /* 0x1F */
+  
+  morkCh_kV|morkCh_kW,     /* 0x20 space */
+  morkCh_kV|morkCh_kM,     /* 0x21 ! */
+  morkCh_kV,               /* 0x22 " */
+  morkCh_kV,               /* 0x23 # */
+  0,                       /* 0x24 $ cannot be kV because needs escape */
+  morkCh_kV,               /* 0x25 % */
+  morkCh_kV,               /* 0x26 & */
+  morkCh_kV,               /* 0x27 ' */
+  morkCh_kV,               /* 0x28 ( */
+  0,                       /* 0x29 ) cannot be kV because needs escape */
+  morkCh_kV,               /* 0x2A * */
+  morkCh_kV|morkCh_kM,     /* 0x2B + */
+  morkCh_kV,               /* 0x2C , */
+  morkCh_kV|morkCh_kM,     /* 0x2D - */
+  morkCh_kV,               /* 0x2E . */
+  morkCh_kV,               /* 0x2F / */
+  
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x30 0 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x31 1 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x32 2 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x33 3 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x34 4 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x35 5 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x36 6 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x37 7 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x38 8 */
+  morkCh_kV|morkCh_kD|morkCh_kX,  /* 0x39 9 */
+  morkCh_kV|morkCh_kN|morkCh_kM,        /* 0x3A : */
+  morkCh_kV,                /* 0x3B ; */
+  morkCh_kV,                /* 0x3C < */
+  morkCh_kV,                /* 0x3D = */
+  morkCh_kV,                /* 0x3E > */
+  morkCh_kV|morkCh_kM,      /* 0x3F ? */
+  
+  morkCh_kV,                /* 0x40 @  */  
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX,  /* 0x41 A */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX,  /* 0x42 B */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX,  /* 0x43 C */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX,  /* 0x44 D */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX,  /* 0x45 E */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX,  /* 0x46 F */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x47 G */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x48 H */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x49 I */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x4A J */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x4B K */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x4C L */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x4D M */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x4E N */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x4F O */
+  
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x50 P */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x51 Q */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x52 R */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x53 S */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x54 T */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x55 U */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x56 V */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x57 W */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x58 X */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x59 Y */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU,          /* 0x5A Z */
+  morkCh_kV,                /* 0x5B [ */
+  0,                /* 0x5C \ cannot be kV because needs escape */
+  morkCh_kV,                /* 0x5D ] */
+  morkCh_kV,          /* 0x5E ^ */
+  morkCh_kV|morkCh_kN|morkCh_kM,          /* 0x5F _ */
+  
+  morkCh_kV,                /* 0x60 ` */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX,  /* 0x61 a */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX,  /* 0x62 b */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX,  /* 0x63 c */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX,  /* 0x64 d */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX,  /* 0x65 e */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX,  /* 0x66 f */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x67 g */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x68 h */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x69 i */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x6A j */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x6B k */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x6C l */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x6D m */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x6E n */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x6F o */
+  
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x70 p */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x71 q */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x72 r */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x73 s */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x74 t */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x75 u */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x76 v */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x77 w */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x78 x */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x79 y */
+  morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL,          /* 0x7A z */
+  morkCh_kV,                /* 0x7B { */
+  morkCh_kV,                /* 0x7C | */
+  morkCh_kV,                /* 0x7D } */
+  morkCh_kV,          /* 0x7E ~ */
+  morkCh_kW,          /* 0x7F rubout */
+
+/* $"80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F"   */
+  0,    0,    0,    0,    0,    0,    0,    0,  
+  0,    0,    0,    0,    0,    0,    0,    0,  
+
+/* $"90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F"   */
+  0,    0,    0,    0,    0,    0,    0,    0,  
+  0,    0,    0,    0,    0,    0,    0,    0,  
+
+/* $"A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF"   */
+  0,    0,    0,    0,    0,    0,    0,    0,  
+  0,    0,    0,    0,    0,    0,    0,    0,  
+
+/* $"B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF"   */
+  0,    0,    0,    0,    0,    0,    0,    0,  
+  0,    0,    0,    0,    0,    0,    0,    0,  
+
+/* $"C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF"   */
+  0,    0,    0,    0,    0,    0,    0,    0,  
+  0,    0,    0,    0,    0,    0,    0,    0,  
+
+/* $"D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF"   */
+  0,    0,    0,    0,    0,    0,    0,    0,  
+  0,    0,    0,    0,    0,    0,    0,    0,  
+
+/* $"E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF"   */
+  0,    0,    0,    0,    0,    0,    0,    0,  
+  0,    0,    0,    0,    0,    0,    0,    0,  
+
+/* $"F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF"   */
+  0,    0,    0,    0,    0,    0,    0,    0,  
+  0,    0,    0,    0,    0,    0,    0,    0,  
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkCh.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+ 
+#ifndef _MORKCH_
+#define _MORKCH_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+/* this byte char predicate header file derives from public domain Mithril */
+/* (that means much of this has a copyright dedicated to the public domain) */
+
+/* Use all 8 pred bits; lose some pred bits only if we need to reuse them. */
+
+/* ch pred bits: W:white D:digit V:value U:upper L:lower N:name  M:more */
+#define morkCh_kW      (1 << 0)
+#define morkCh_kD      (1 << 1)
+#define morkCh_kV      (1 << 2)
+#define morkCh_kU      (1 << 3)
+#define morkCh_kL      (1 << 4)
+#define morkCh_kX      (1 << 5)
+#define morkCh_kN      (1 << 6)
+#define morkCh_kM      (1 << 7)
+
+extern const mork_flags morkCh_Type[]; /* 256 byte predicate bits ch map */
+
+/* is a numeric decimal digit: (note memory access might be slower) */
+/* define morkCh_IsDigit(c)  ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kD ) */
+#define morkCh_IsDigit(c)    ( ((mork_ch) c) >= '0' && ((mork_ch) c) <= '9' )
+
+/* is a numeric octal digit: */
+#define morkCh_IsOctal(c)    ( ((mork_ch) c) >= '0' && ((mork_ch) c) <= '7' )
+
+/* is a numeric hexadecimal digit: */
+#define morkCh_IsHex(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kX )
+
+/* is value (can be printed in Mork value without needing hex or escape): */
+#define morkCh_IsValue(c)    ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kV )
+
+/* is white space : */
+#define morkCh_IsWhite(c)    ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kW )
+
+/* is name (can start a Mork name): */
+#define morkCh_IsName(c)     ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kN )
+
+/* is name (can continue a Mork name): */
+#define morkCh_IsMore(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kM )
+
+/* is alphabetic upper or lower case */
+#define morkCh_IsAlpha(c)    \
+  ( morkCh_Type[ (mork_ch)(c) ] & (morkCh_kL|morkCh_kU) )
+
+/* is alphanumeric, including lower case, upper case, and digits */
+#define morkCh_IsAlphaNum(c) \
+  (morkCh_Type[ (mork_ch)(c) ]&(morkCh_kL|morkCh_kU|morkCh_kD))
+
+/* ````` repeated testing of predicate bits in single flag byte ````` */
+
+#define morkCh_GetFlags(c) ( morkCh_Type[ (mork_ch)(c) ] )
+
+#define morkFlags_IsDigit(f)    ( (f) & morkCh_kD )
+#define morkFlags_IsHex(f)      ( (f) & morkCh_kX )
+#define morkFlags_IsValue(f)    ( (f) & morkCh_kV )
+#define morkFlags_IsWhite(f)    ( (f) & morkCh_kW )
+#define morkFlags_IsName(f)     ( (f) & morkCh_kN )
+#define morkFlags_IsMore(f)     ( (f) & morkCh_kM )
+#define morkFlags_IsAlpha(f)    ( (f) & (morkCh_kL|morkCh_kU) )
+#define morkFlags_IsAlphaNum(f) ( (f) & (morkCh_kL|morkCh_kU|morkCh_kD) )
+
+#define morkFlags_IsUpper(f)    ( (f) & morkCh_kU )
+#define morkFlags_IsLower(f)    ( (f) & morkCh_kL )
+
+/* ````` character case (e.g. for case insensitive operations) ````` */
+
+  
+#define morkCh_IsAscii(c)         ( ((mork_u1) c) <= 0x7F )
+#define morkCh_IsSevenBitChar(c)  ( ((mork_u1) c) <= 0x7F )
+
+/* ````` character case (e.g. for case insensitive operations) ````` */
+
+#define morkCh_ToLower(c)    ((c)-'A'+'a')
+#define morkCh_ToUpper(c)    ((c)-'a'+'A')
+
+/* extern int morkCh_IsUpper (int c); */
+#define morkCh_IsUpper(c)    ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kU )
+
+/* extern int morkCh_IsLower (int c); */
+#define morkCh_IsLower(c)    ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kL )
+
+#endif
+/* _MORKCH_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkConfig.cpp
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCONFIG_
+#include "morkConfig.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+void mork_assertion_signal(const char* inMessage)
+{
+#if defined(MORK_WIN) || defined(MORK_MAC)
+  // asm { int 3 }
+  NS_ERROR(inMessage);
+#endif /*MORK_WIN*/
+}
+
+#if defined(MORK_OS2)
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <share.h>
+#include <io.h>
+
+FILE* mork_fileopen(const char* name, const char* mode)
+{
+    int access = O_RDWR;
+    int descriptor;
+    int pmode = 0;
+
+    /* Only possible options are wb+ and rb+ */
+    MORK_ASSERT((mode[0] == 'w' || mode[0] == 'r') && (mode[1] == 'b') && (mode[2] == '+'));
+    if (mode[0] == 'w') {
+        access |= (O_TRUNC | O_CREAT);
+        pmode = S_IREAD | S_IWRITE;
+    }
+
+    descriptor = sopen(name, access, SH_DENYNO, pmode);
+    if (descriptor != -1) {
+        return fdopen(descriptor, mode);
+    }
+    return NULL;
+}
+#endif
+
+#ifdef MORK_PROVIDE_STDLIB
+
+MORK_LIB_IMPL(mork_i4)
+mork_memcmp(const void* inOne, const void* inTwo, mork_size inSize)
+{
+  register const mork_u1* t = (const mork_u1*) inTwo;
+  register const mork_u1* s = (const mork_u1*) inOne;
+  const mork_u1* end = s + inSize;
+  register mork_i4 delta;
+  
+  while ( s < end )
+  {
+    delta = ((mork_i4) *s) - ((mork_i4) *t);
+    if ( delta )
+      return delta;
+    else
+    {
+      ++t;
+      ++s;
+    }
+  }
+  return 0;
+}
+
+MORK_LIB_IMPL(void)
+mork_memcpy(void* outDst, const void* inSrc, mork_size inSize)
+{
+  register mork_u1* d = (mork_u1*) outDst;
+  mork_u1* end = d + inSize;
+  register const mork_u1* s = ((const mork_u1*) inSrc);
+  
+  while ( inSize >= 8 )
+  {
+    *d++ = *s++;
+    *d++ = *s++;
+    *d++ = *s++;
+    *d++ = *s++;
+    
+    *d++ = *s++;
+    *d++ = *s++;
+    *d++ = *s++;
+    *d++ = *s++;
+    
+    inSize -= 8;
+  }
+  
+  while ( d < end )
+    *d++ = *s++;
+}
+
+MORK_LIB_IMPL(void)
+mork_memmove(void* outDst, const void* inSrc, mork_size inSize)
+{
+  register mork_u1* d = (mork_u1*) outDst;
+  register const mork_u1* s = (const mork_u1*) inSrc;
+  if ( d != s && inSize ) // copy is necessary?
+  {
+    const mork_u1* srcEnd = s + inSize; // one past last source byte
+    
+    if ( d > s && d < srcEnd ) // overlap? need to copy backwards?
+    {
+      s = srcEnd; // start one past last source byte
+      d += inSize; // start one past last dest byte
+      mork_u1* dstBegin = d; // last byte to write is first in dest range
+      while ( d - dstBegin >= 8 )
+      {
+        *--d = *--s;
+        *--d = *--s;
+        *--d = *--s;
+        *--d = *--s;
+        
+        *--d = *--s;
+        *--d = *--s;
+        *--d = *--s;
+        *--d = *--s;
+      }
+      while ( d > dstBegin )
+        *--d = *--s;
+    }
+    else // can copy forwards without any overlap
+    {
+      mork_u1* dstEnd = d + inSize;
+      while ( dstEnd - d >= 8 )
+      {
+        *d++ = *s++;
+        *d++ = *s++;
+        *d++ = *s++;
+        *d++ = *s++;
+        
+        *d++ = *s++;
+        *d++ = *s++;
+        *d++ = *s++;
+        *d++ = *s++;
+      }
+      while ( d < dstEnd )
+        *d++ = *s++;
+    }
+  }
+}
+
+MORK_LIB_IMPL(void)
+mork_memset(void* outDst, int inByte, mork_size inSize)
+{
+  register mork_u1* d = (mork_u1*) outDst;
+  mork_u1* end = d + inSize;
+  while ( d < end )
+    *d++ = (mork_u1) inByte;
+}
+
+MORK_LIB_IMPL(void)
+mork_strcpy(void* outDst, const void* inSrc)
+{
+  // back up one first to support preincrement
+  register mork_u1* d = ((mork_u1*) outDst) - 1;
+  register const mork_u1* s = ((const mork_u1*) inSrc) - 1;
+  while ( ( *++d = *++s ) != 0 )
+    /* empty */;
+}
+
+MORK_LIB_IMPL(mork_i4)
+mork_strcmp(const void* inOne, const void* inTwo)
+{
+  register const mork_u1* t = (const mork_u1*) inTwo;
+  register const mork_u1* s = ((const mork_u1*) inOne);
+  register mork_i4 a;
+  register mork_i4 b;
+  register mork_i4 delta;
+  
+  do
+  {
+    a = (mork_i4) *s++;
+    b = (mork_i4) *t++;
+    delta = a - b;
+  }
+  while ( !delta && a && b );
+  
+  return delta;
+}
+
+MORK_LIB_IMPL(mork_i4)
+mork_strncmp(const void* inOne, const void* inTwo, mork_size inSize)
+{
+  register const mork_u1* t = (const mork_u1*) inTwo;
+  register const mork_u1* s = (const mork_u1*) inOne;
+  const mork_u1* end = s + inSize;
+  register mork_i4 delta;
+  register mork_i4 a;
+  register mork_i4 b;
+  
+  while ( s < end )
+  {
+    a = (mork_i4) *s++;
+    b = (mork_i4) *t++;
+    delta = a - b;
+    if ( delta || !a || !b )
+      return delta;
+  }
+  return 0;
+}
+
+MORK_LIB_IMPL(mork_size)
+mork_strlen(const void* inString)
+{
+  // back up one first to support preincrement
+  register const mork_u1* s = ((const mork_u1*) inString) - 1;
+  while ( *++s ) // preincrement is cheapest
+    /* empty */;
+  
+  return s - ((const mork_u1*) inString); // distance from original address
+}
+
+#endif /*MORK_PROVIDE_STDLIB*/
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkConfig.h
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKCONFIG_
+#define _MORKCONFIG_ 1
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// { %%%%% begin debug mode options in Mork %%%%%
+#define MORK_DEBUG 1
+// } %%%%% end debug mode options in Mork %%%%%
+
+#ifdef MORK_DEBUG
+#define MORK_MAX_CODE_COMPILE 1
+#endif
+
+// { %%%%% begin platform defs peculiar to Mork %%%%%
+
+#ifdef XP_MACOSX
+#define MORK_MAC 1
+#endif
+
+#ifdef XP_OS2
+#define MORK_OS2 1
+#endif
+
+#ifdef XP_WIN
+#define MORK_WIN 1
+#endif
+
+#ifdef XP_UNIX
+#define MORK_UNIX 1
+#endif
+
+// } %%%%% end platform defs peculiar to Mork %%%%%
+
+#if defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC) || defined(MORK_OS2)
+#include <stdio.h> 
+#include <ctype.h> 
+#include <errno.h> 
+#include <string.h> 
+#ifdef HAVE_MEMORY_H
+#include <memory.h> 
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>  /* for SEEK_SET, SEEK_END */
+#endif
+
+#include "nsDebug.h" 
+
+#define MORK_ISPRINT(c) isprint(c) 
+
+#define MORK_FILETELL(file) ftell(file) 
+#define MORK_FILESEEK(file, where, how) fseek(file, where, how) 
+#define MORK_FILEREAD(outbuf, insize, file) fread(outbuf, 1, insize, file) 
+#if defined(MORK_WIN)
+void mork_fileflush(FILE * file);
+#define MORK_FILEFLUSH(file) mork_fileflush(file) 
+#else
+#define MORK_FILEFLUSH(file) fflush(file) 
+#endif /*MORK_WIN*/
+
+#if defined(MORK_OS2)
+FILE* mork_fileopen(const char* name, const char* mode);
+#define MORK_FILEOPEN(file, how) mork_fileopen(file, how) 
+#else
+#define MORK_FILEOPEN(file, how) fopen(file, how) 
+#endif
+#define MORK_FILECLOSE(file) fclose(file) 
+#endif /*MORK_WIN*/
+
+/* ===== separating switchable features ===== */
+
+#define MORK_ENABLE_ZONE_ARENAS 1 /* using morkZone for pooling */
+
+//#define MORK_ENABLE_PROBE_MAPS 1 /* use smaller hash tables */
+
+#define MORK_BEAD_OVER_NODE_MAPS 1 /* use bead not node maps */
+
+/* ===== pooling ===== */
+
+#if defined(HAVE_64BIT_OS)
+#define MORK_CONFIG_ALIGN_8 1 /* must have 8 byte alignment */
+#else
+#define MORK_CONFIG_PTR_SIZE_4 1 /* sizeof(void*) == 4 */
+#endif
+
+// #define MORK_DEBUG_HEAP_STATS 1 /* analyze per-block heap usage */
+
+/* ===== ===== ===== ===== line characters ===== ===== ===== ===== */
+#define mork_kCR 0x0D
+#define mork_kLF 0x0A
+#define mork_kVTAB '\013'
+#define mork_kFF '\014'
+#define mork_kTAB '\011'
+#define mork_kCRLF "\015\012"     /* A CR LF equivalent string */
+
+#if defined(MORK_MAC)
+#  define mork_kNewline             "\015"
+#  define mork_kNewlineSize 1
+#else
+#  if defined(MORK_WIN) || defined(MORK_OS2)
+#    define mork_kNewline           "\015\012"
+#    define mork_kNewlineSize       2
+#  else
+#    if defined(MORK_UNIX)
+#      define mork_kNewline         "\012"
+#      define mork_kNewlineSize     1
+#    endif /* MORK_UNIX */
+#  endif /* MORK_WIN */
+#endif /* MORK_MAC */
+
+// { %%%%% begin assertion macro %%%%%
+extern void mork_assertion_signal(const char* inMessage);
+#define MORK_ASSERTION_SIGNAL(Y) mork_assertion_signal(Y)
+#define MORK_ASSERT(X) if (!(X)) MORK_ASSERTION_SIGNAL(#X)
+// } %%%%% end assertion macro %%%%%
+
+#define MORK_LIB(return) return /*API return declaration*/
+#define MORK_LIB_IMPL(return) return /*implementation return declaration*/
+
+// { %%%%% begin standard c utility methods %%%%%
+
+#if defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC) || defined(MORK_OS2)
+#define MORK_USE_C_STDLIB 1
+#endif /*MORK_WIN*/
+
+#ifdef MORK_USE_C_STDLIB
+#define MORK_MEMCMP(src1,src2,size)  memcmp(src1,src2,size)
+#define MORK_MEMCPY(dest,src,size)   memcpy(dest,src,size)
+#define MORK_MEMMOVE(dest,src,size)  memmove(dest,src,size)
+#define MORK_MEMSET(dest,byte,size)  memset(dest,byte,size)
+#define MORK_STRCPY(dest,src)        strcpy(dest,src)
+#define MORK_STRCMP(one,two)         strcmp(one,two)
+#define MORK_STRNCMP(one,two,length) strncmp(one,two,length)
+#define MORK_STRLEN(string)          strlen(string)
+#endif /*MORK_USE_C_STDLIB*/
+
+#ifdef MORK_PROVIDE_STDLIB
+MORK_LIB(mork_i4) mork_memcmp(const void* a, const void* b, mork_size inSize);
+MORK_LIB(void) mork_memcpy(void* dst, const void* src, mork_size inSize);
+MORK_LIB(void) mork_memmove(void* dst, const void* src, mork_size inSize);
+MORK_LIB(void) mork_memset(void* dst, int inByte, mork_size inSize);
+MORK_LIB(void) mork_strcpy(void* dst, const void* src);
+MORK_LIB(mork_i4) mork_strcmp(const void* a, const void* b);
+MORK_LIB(mork_i4) mork_strncmp(const void* a, const void* b, mork_size inSize);
+MORK_LIB(mork_size) mork_strlen(const void* inString);
+
+#define MORK_MEMCMP(src1,src2,size)  mork_memcmp(src1,src2,size)
+#define MORK_MEMCPY(dest,src,size)   mork_memcpy(dest,src,size)
+#define MORK_MEMMOVE(dest,src,size)  mork_memmove(dest,src,size)
+#define MORK_MEMSET(dest,byte,size)  mork_memset(dest,byte,size)
+#define MORK_STRCPY(dest,src)        mork_strcpy(dest,src)
+#define MORK_STRCMP(one,two)         mork_strcmp(one,two)
+#define MORK_STRNCMP(one,two,length) mork_strncmp(one,two,length)
+#define MORK_STRLEN(string)          mork_strlen(string)
+#endif /*MORK_PROVIDE_STDLIB*/
+
+// } %%%%% end standard c utility methods %%%%%
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKCONFIG_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkCursor.cpp
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkCursor::CloseMorkNode(morkEnv* ev) // CloseCursor() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseCursor(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkCursor::~morkCursor() // assert CloseCursor() executed earlier
+{
+}
+
+/*public non-poly*/
+morkCursor::morkCursor(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mCursor_Seed( 0 )
+, mCursor_Pos( -1 )
+, mCursor_DoFailOnSeedOutOfSync( morkBool_kFalse )
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kCursor;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkCursor, morkObject, nsIMdbCursor)
+
+/*public non-poly*/ void
+morkCursor::CloseCursor(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mCursor_Seed = 0;
+      mCursor_Pos = -1;
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// { ----- begin ref counting for well-behaved cyclic graphs -----
+NS_IMETHODIMP
+morkCursor::GetWeakRefCount(nsIMdbEnv* mev, // weak refs
+  mdb_count* outCount)
+{
+  *outCount = WeakRefsOnly();
+  return NS_OK;
+}  
+NS_IMETHODIMP
+morkCursor::GetStrongRefCount(nsIMdbEnv* mev, // strong refs
+  mdb_count* outCount)
+{
+  *outCount = StrongRefsOnly();
+  return NS_OK;
+}
+// ### TODO - clean up this cast, if required
+NS_IMETHODIMP
+morkCursor::AddWeakRef(nsIMdbEnv* mev)
+{
+  return morkNode::AddWeakRef((morkEnv *) mev);
+}
+NS_IMETHODIMP
+morkCursor::AddStrongRef(nsIMdbEnv* mev)
+{
+  return morkNode::AddStrongRef((morkEnv *) mev);
+}
+
+NS_IMETHODIMP
+morkCursor::CutWeakRef(nsIMdbEnv* mev)
+{
+  return morkNode::CutWeakRef((morkEnv *) mev);
+}
+NS_IMETHODIMP
+morkCursor::CutStrongRef(nsIMdbEnv* mev)
+{
+  return morkNode::CutStrongRef((morkEnv *) mev);
+}
+
+  
+NS_IMETHODIMP
+morkCursor::CloseMdbObject(nsIMdbEnv* mev)
+{
+  return morkNode::CloseMdbObject((morkEnv *) mev);
+}
+
+NS_IMETHODIMP
+morkCursor::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
+{
+  *outOpen = IsOpenNode();
+  return NS_OK;
+}
+NS_IMETHODIMP
+morkCursor::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly)
+{
+  *outIsReadonly = IsFrozen();
+  return NS_OK;
+}
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+NS_IMETHODIMP
+morkCursor::GetCount(nsIMdbEnv* mev, mdb_count* outCount)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::SetPos(nsIMdbEnv* mev, mdb_pos inPos)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::GetPos(nsIMdbEnv* mev, mdb_pos* outPos)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::SetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool inFail)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::GetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool* outFail)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkCursor.h
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKCURSOR_
+#define _MORKCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kCursor  /*i*/ 0x4375 /* ascii 'Cu' */
+
+class morkCursor : public morkObject, public nsIMdbCursor{ // collection iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+  // mork_color   mBead_Color;   // ID for this bead
+  // morkHandle*  mObject_Handle;  // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly);
+  // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+  // } ----- end attribute methods -----
+
+  // { ----- begin ref counting for well-behaved cyclic graphs -----
+  NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
+    mdb_count* outCount);  
+  NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
+    mdb_count* outCount);
+
+  NS_IMETHOD AddWeakRef(nsIMdbEnv* ev);
+  NS_IMETHOD AddStrongRef(nsIMdbEnv* ev);
+
+  NS_IMETHOD CutWeakRef(nsIMdbEnv* ev);
+  NS_IMETHOD CutStrongRef(nsIMdbEnv* ev);
+  
+  NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev); // called at strong refs zero
+  NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen);
+  // } ----- end ref counting -----
+  
+// } ===== end nsIMdbObject methods =====
+
+// { ===== begin nsIMdbCursor methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount); // readonly
+  NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed);    // readonly
+  
+  NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos);   // mutable
+  NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos);
+  
+  NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail);
+  NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail);
+  // } ----- end attribute methods -----
+
+// } ===== end nsIMdbCursor methods =====
+    
+  // } ----- end attribute methods -----
+
+  mork_seed  mCursor_Seed;
+  mork_pos   mCursor_Pos;
+  mork_bool  mCursor_DoFailOnSeedOutOfSync;
+  mork_u1    mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseCursor() only if open
+  virtual ~morkCursor(); // assert that CloseCursor() executed earlier
+  
+public: // morkCursor construction & destruction
+  morkCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+  void CloseCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkCursor(const morkCursor& other);
+  morkCursor& operator=(const morkCursor& other);
+
+public: // dynamic type identification
+  mork_bool IsCursor() const
+  { return IsNode() && mNode_Derived == morkDerived_kCursor; }
+// } ===== end morkNode methods =====
+
+public: // other cursor methods
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakCursor(morkCursor* me,
+    morkEnv* ev, morkCursor** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongCursor(morkCursor* me,
+    morkEnv* ev, morkCursor** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKCURSOR_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkDeque.cpp
@@ -0,0 +1,300 @@
+/*************************************************************************
+This software is part of a public domain IronDoc source code distribution,
+and is provided on an "AS IS" basis, with all risks borne by the consumers
+or users of the IronDoc software.  There are no warranties, guarantees, or
+promises about quality of any kind; and no remedies for failure exist. 
+
+Permission is hereby granted to use this IronDoc software for any purpose
+at all, without need for written agreements, without royalty or license
+fees, and without fees or obligations of any other kind.  Anyone can use,
+copy, change and distribute this software for any purpose, and nothing is
+required, implicitly or otherwise, in exchange for this usage.
+
+You cannot apply your own copyright to this software, but otherwise you
+are encouraged to enjoy the use of this software in any way you see fit.
+However, it would be rude to remove names of developers from the code.
+(IronDoc is also known by the short name "Fe" and a longer name "Ferrum",
+which are used interchangeably with the name IronDoc in the sources.)
+*************************************************************************/
+/*
+ * File:      morkDeque.cpp
+ * Contains:  Ferrum deque (double ended queue (linked list))
+ *
+ * Copied directly from public domain IronDoc, with minor naming tweaks:
+ * Designed and written by David McCusker, but all this code is public domain.
+ * There are no warranties, no guarantees, no promises, and no remedies.
+ */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+/*=============================================================================
+ * morkNext: linked list node for very simple, singly-linked list
+ */
+ 
+morkNext::morkNext() : mNext_Link( 0 )
+{
+}
+
+/*static*/ void*
+morkNext::MakeNewNext(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev)
+{
+  void* next = 0;
+  if ( &ioHeap )
+  {
+    ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**) &next);
+    if ( !next )
+      ev->OutOfMemoryError();
+  }
+  else
+    ev->NilPointerError();
+  
+  return next;
+}
+
+/*static*/
+void morkNext::ZapOldNext(morkEnv* ev, nsIMdbHeap* ioHeap)
+{
+  if ( ioHeap )
+  {
+    if ( this )
+      ioHeap->Free(ev->AsMdbEnv(), this);
+  }
+  else
+    ev->NilPointerError();
+}
+
+/*=============================================================================
+ * morkList: simple, singly-linked list
+ */
+
+morkList::morkList() : mList_Head( 0 ), mList_Tail( 0 )
+{
+}
+
+void morkList::CutAndZapAllListMembers(morkEnv* ev, nsIMdbHeap* ioHeap)
+// make empty list, zapping every member by calling ZapOldNext()
+{
+  if ( ioHeap )
+  {
+    morkNext* next = 0;
+    while ( (next = this->PopHead()) != 0 )
+      next->ZapOldNext(ev, ioHeap);
+      
+    mList_Head = 0;
+    mList_Tail = 0;
+  }
+  else
+    ev->NilPointerError();
+}
+
+void morkList::CutAllListMembers()
+// just make list empty, dropping members without zapping
+{
+  while ( this->PopHead() )
+    /* empty */;
+
+  mList_Head = 0;
+  mList_Tail = 0;
+}
+
+morkNext* morkList::PopHead() // cut head of list
+{
+  morkNext* outHead = mList_Head;
+  if ( outHead ) // anything to cut from list?
+  {
+    morkNext* next = outHead->mNext_Link;
+    mList_Head = next;
+    if ( !next ) // cut the last member, so tail no longer exists?
+      mList_Tail = 0;
+      
+    outHead->mNext_Link = 0; // nil outgoing node link; unnecessary, but tidy
+  }
+  return outHead;
+}
+
+
+void morkList::PushHead(morkNext* ioLink) // add to head of list
+{
+  morkNext* head = mList_Head; // old head of list
+  morkNext* tail = mList_Tail; // old tail of list
+  
+  MORK_ASSERT( (head && tail) || (!head && !tail));
+  
+  ioLink->mNext_Link = head; // make old head follow the new link
+  if ( !head ) // list was previously empty?
+    mList_Tail = ioLink; // head is also tail for first member added
+
+  mList_Head = ioLink; // head of list is the new link
+}
+
+void morkList::PushTail(morkNext* ioLink) // add to tail of list
+{
+  morkNext* head = mList_Head; // old head of list
+  morkNext* tail = mList_Tail; // old tail of list
+  
+  MORK_ASSERT( (head && tail) || (!head && !tail));
+  
+  ioLink->mNext_Link = 0; 
+  if ( tail ) 
+  {
+	  tail->mNext_Link = ioLink;
+	  mList_Tail = ioLink;
+  }
+  else // list was previously empty?
+	  mList_Head = mList_Tail = ioLink; // tail is also head for first member added
+}
+
+/*=============================================================================
+ * morkLink: linked list node embedded in objs to allow insertion in morkDeques
+ */
+ 
+morkLink::morkLink() : mLink_Next( 0 ), mLink_Prev( 0 )
+{
+}
+
+/*static*/ void*
+morkLink::MakeNewLink(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev)
+{
+  void* alink = 0;
+  if ( &ioHeap )
+  {
+    ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**) &alink);
+    if ( !alink )
+      ev->OutOfMemoryError();
+  }
+  else
+    ev->NilPointerError();
+  
+  return alink;
+}
+
+/*static*/
+void morkLink::ZapOldLink(morkEnv* ev, nsIMdbHeap* ioHeap)
+{
+  if ( ioHeap )
+  {
+    if ( this )
+      ioHeap->Free(ev->AsMdbEnv(), this);
+  }
+  else
+    ev->NilPointerError();
+}
+  
+/*=============================================================================
+ * morkDeque: doubly linked list modeled after VAX queue instructions
+ */
+
+morkDeque::morkDeque()
+{
+  mDeque_Head.SelfRefer();
+}
+
+
+/*| RemoveFirst: 
+|*/
+morkLink*
+morkDeque::RemoveFirst() /*i*/
+{
+  morkLink* alink = mDeque_Head.mLink_Next;
+  if ( alink != &mDeque_Head )
+  {
+    (mDeque_Head.mLink_Next = alink->mLink_Next)->mLink_Prev = 
+      &mDeque_Head;
+    return alink;
+  }
+  return (morkLink*) 0;
+}
+
+/*| RemoveLast: 
+|*/
+morkLink*
+morkDeque::RemoveLast() /*i*/
+{
+  morkLink* alink = mDeque_Head.mLink_Prev;
+  if ( alink != &mDeque_Head )
+  {
+    (mDeque_Head.mLink_Prev = alink->mLink_Prev)->mLink_Next = 
+      &mDeque_Head;
+    return alink;
+  }
+  return (morkLink*) 0;
+}
+
+/*| At: 
+|*/
+morkLink*
+morkDeque::At(mork_pos index) const /*i*/
+  /* indexes are one based (and not zero based) */
+{ 
+  register mork_num count = 0;
+  register morkLink* alink;
+  for ( alink = this->First(); alink; alink = this->After(alink) )
+  {
+    if ( ++count == (mork_num) index )
+      break;
+  }
+  return alink;
+}
+
+/*| IndexOf: 
+|*/
+mork_pos
+morkDeque::IndexOf(const morkLink* member) const /*i*/
+  /* indexes are one based (and not zero based) */
+  /* zero means member is not in deque */
+{ 
+  register mork_num count = 0;
+  register const morkLink* alink;
+  for ( alink = this->First(); alink; alink = this->After(alink) )
+  {
+    ++count;
+    if ( member == alink )
+      return (mork_pos) count;
+  }
+  return 0;
+}
+
+/*| Length: 
+|*/
+mork_num
+morkDeque::Length() const /*i*/
+{ 
+  register mork_num count = 0;
+  register morkLink* alink;
+  for ( alink = this->First(); alink; alink = this->After(alink) )
+    ++count;
+  return count;
+}
+
+/*| LengthCompare: 
+|*/
+int
+morkDeque::LengthCompare(mork_num c) const /*i*/
+{ 
+  register mork_num count = 0;
+  register const morkLink* alink;
+  for ( alink = this->First(); alink; alink = this->After(alink) )
+  {
+    if ( ++count > c )
+      return 1;
+  }
+  return ( count == c )? 0 : -1;
+}
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkDeque.h
@@ -0,0 +1,239 @@
+/*************************************************************************
+This software is part of a public domain IronDoc source code distribution,
+and is provided on an "AS IS" basis, with all risks borne by the consumers
+or users of the IronDoc software.  There are no warranties, guarantees, or
+promises about quality of any kind; and no remedies for failure exist. 
+
+Permission is hereby granted to use this IronDoc software for any purpose
+at all, without need for written agreements, without royalty or license
+fees, and without fees or obligations of any other kind.  Anyone can use,
+copy, change and distribute this software for any purpose, and nothing is
+required, implicitly or otherwise, in exchange for this usage.
+
+You cannot apply your own copyright to this software, but otherwise you
+are encouraged to enjoy the use of this software in any way you see fit.
+However, it would be rude to remove names of developers from the code.
+(IronDoc is also known by the short name "Fe" and a longer name "Ferrum",
+which are used interchangeably with the name IronDoc in the sources.)
+*************************************************************************/
+/*
+ * File:      morkDeque.h 
+ * Contains:  Ferrum deque (double ended queue (linked list))
+ *
+ * Copied directly from public domain IronDoc, with minor naming tweaks:
+ * Designed and written by David McCusker, but all this code is public domain.
+ * There are no warranties, no guarantees, no promises, and no remedies.
+ */
+
+#ifndef _MORKDEQUE_
+#define _MORKDEQUE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+/*=============================================================================
+ * morkNext: linked list node for very simple, singly-linked list
+ */
+
+class morkNext /*d*/ {  
+public:
+  morkNext*  mNext_Link;
+  
+public:
+  morkNext(int inZero) : mNext_Link( 0 ) { }
+  
+  morkNext(morkNext* ioLink) : mNext_Link( ioLink ) { }
+  
+  morkNext(); // mNext_Link( 0 ), { }
+  
+public:
+  morkNext*  GetNextLink() const { return mNext_Link; }
+  
+public: // link memory management methods
+  static void* MakeNewNext(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev);
+  void ZapOldNext(morkEnv* ev, nsIMdbHeap* ioHeap);
+
+public: // link memory management operators
+  void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+  { return morkNext::MakeNewNext(inSize, ioHeap, ev); }
+  
+  void operator delete(void* ioAddress) // DO NOT CALL THIS, hope to crash:
+  { ((morkNext*) 0)->ZapOldNext((morkEnv*) 0, (nsIMdbHeap*) 0); } // boom
+};
+
+/*=============================================================================
+ * morkList: simple, singly-linked list
+ */
+
+/*| morkList: a list of singly-linked members (instances of morkNext), where
+**| the number of list members might be so numerous that we must about cost
+**| for two pointer link slots per member (as happens with morkLink).
+**|
+**|| morkList is intended to support lists of changes in morkTable, where we
+**| are worried about the space cost of representing such changes. (Later we
+**| can use an array instead, when we get even more worried, to avoid cost
+**| of link slots at all, per member).
+**|
+**|| Do NOT create cycles in links using this list class, since we do not
+**| deal with them very nicely.
+|*/
+class morkList /*d*/  {  
+public:
+  morkNext*  mList_Head; // first link in the list
+  morkNext*  mList_Tail; // last link in the list
+  
+public:
+  morkNext*  GetListHead() const { return mList_Head; }
+  morkNext*  GetListTail() const { return mList_Tail; }
+
+  mork_bool IsListEmpty() const { return ( mList_Head == 0 ); }
+  mork_bool HasListMembers() const { return ( mList_Head != 0 ); }
+  
+public:
+  morkList(); // : mList_Head( 0 ), mList_Tail( 0 ) { }
+  
+  void CutAndZapAllListMembers(morkEnv* ev, nsIMdbHeap* ioHeap);
+  // make empty list, zapping every member by calling ZapOldNext()
+
+  void CutAllListMembers();
+  // just make list empty, dropping members without zapping
+
+public:
+  morkNext* PopHead(); // cut head of list
+  
+  // Note we don't support PopTail(), so use morkDeque if you need that.
+  
+  void PushHead(morkNext* ioLink); // add to head of list
+  void PushTail(morkNext* ioLink); // add to tail of list
+};
+
+/*=============================================================================
+ * morkLink: linked list node embedded in objs to allow insertion in morkDeques
+ */
+
+class morkLink /*d*/ {  
+public:
+  morkLink*  mLink_Next;
+  morkLink*  mLink_Prev;
+  
+public:
+  morkLink(int inZero) : mLink_Next( 0 ), mLink_Prev( 0 ) { }
+  
+  morkLink(); // mLink_Next( 0 ), mLink_Prev( 0 ) { }
+  
+public:
+  morkLink*  Next() const { return mLink_Next; }
+  morkLink*  Prev() const { return mLink_Prev; }
+  
+  void SelfRefer() { mLink_Next = mLink_Prev = this; }
+  void Clear() { mLink_Next = mLink_Prev = 0; }
+  
+  void AddBefore(morkLink* old)
+  {
+    ((old)->mLink_Prev->mLink_Next = (this))->mLink_Prev = (old)->mLink_Prev;
+    ((this)->mLink_Next = (old))->mLink_Prev = this;
+  }
+  
+  void AddAfter(morkLink* old)
+  {
+    ((old)->mLink_Next->mLink_Prev = (this))->mLink_Next = (old)->mLink_Next;
+    ((this)->mLink_Prev = (old))->mLink_Next = this;
+  }
+  
+  void Remove()
+  {
+    (mLink_Prev->mLink_Next = mLink_Next)->mLink_Prev = mLink_Prev;
+  }
+  
+public: // link memory management methods
+  static void* MakeNewLink(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev);
+  void ZapOldLink(morkEnv* ev, nsIMdbHeap* ioHeap);
+
+public: // link memory management operators
+  void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+  { return morkLink::MakeNewLink(inSize, ioHeap, ev); }
+  
+};
+
+/*=============================================================================
+ * morkDeque: doubly linked list modeled after VAX queue instructions
+ */
+
+class morkDeque /*d*/ {
+public:
+  morkLink  mDeque_Head;
+
+public: // construction
+  morkDeque(); // { mDeque_Head.SelfRefer(); }
+
+public:// methods
+  morkLink* RemoveFirst();
+
+  morkLink* RemoveLast();
+
+  morkLink* At(mork_pos index) const ; /* one-based, not zero-based */
+
+  mork_pos IndexOf(const morkLink* inMember) const; 
+    /* one-based index ; zero means member is not in deque */
+
+  mork_num Length() const;
+
+  /* the following method is more efficient for long lists: */
+  int LengthCompare(mork_num inCount) const;
+  /* -1: length < count, 0: length == count,  1: length > count */
+
+public: // inlines
+
+  mork_bool IsEmpty()const 
+  { return (mDeque_Head.mLink_Next == (morkLink*) &mDeque_Head); }
+
+  morkLink* After(const morkLink* old) const
+  { return (((old)->mLink_Next != &mDeque_Head)?
+            (old)->mLink_Next : (morkLink*) 0); }
+
+  morkLink* Before(const morkLink* old) const
+  { return (((old)->mLink_Prev != &mDeque_Head)?
+            (old)->mLink_Prev : (morkLink*) 0); }
+
+  morkLink*  First() const
+  { return ((mDeque_Head.mLink_Next != &mDeque_Head)?
+    mDeque_Head.mLink_Next : (morkLink*) 0); }
+
+  morkLink*  Last() const
+  { return ((mDeque_Head.mLink_Prev != &mDeque_Head)?
+    mDeque_Head.mLink_Prev : (morkLink*) 0); }
+    
+/*
+From IronDoc documentation for AddFirst:
++--------+   +--------+      +--------+   +--------+   +--------+
+| h.next |-->| b.next |      | h.next |-->| a.next |-->| b.next |
++--------+   +--------+  ==> +--------+   +--------+   +--------+
+| h.prev |<--| b.prev |      | h.prev |<--| a.prev |<--| b.prev |
++--------+   +--------+      +--------+   +--------+   +--------+
+*/
+
+  void AddFirst(morkLink* in) /*i*/ 
+  {
+    ( (mDeque_Head.mLink_Next->mLink_Prev = 
+      (in))->mLink_Next = mDeque_Head.mLink_Next, 
+        ((in)->mLink_Prev = &mDeque_Head)->mLink_Next = (in) );
+  }
+/*
+From IronDoc documentation for AddLast:
++--------+   +--------+      +--------+   +--------+   +--------+
+| y.next |-->| h.next |      | y.next |-->| z.next |-->| h.next |
++--------+   +--------+  ==> +--------+   +--------+   +--------+
+| y.prev |<--| h.prev |      | y.prev |<--| z.prev |<--| h.prev |
++--------+   +--------+      +--------+   +--------+   +--------+
+*/
+
+  void AddLast(morkLink* in)
+  {
+    ( (mDeque_Head.mLink_Prev->mLink_Next = 
+      (in))->mLink_Prev = mDeque_Head.mLink_Prev, 
+        ((in)->mLink_Next = &mDeque_Head)->mLink_Prev = (in) );
+  }
+};
+
+#endif /* _MORKDEQUE_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkEnv.cpp
@@ -0,0 +1,667 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKCH_
+#include "morkCh.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKFACTORY_
+#include "morkFactory.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkEnv::CloseMorkNode(morkEnv* ev) /*i*/ // CloseEnv() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseEnv(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkEnv::~morkEnv() /*i*/ // assert CloseEnv() executed earlier
+{
+  CloseMorkNode(mMorkEnv);
+  if (mEnv_Heap)
+  {
+    mork_bool ownsHeap = mEnv_OwnsHeap;
+    nsIMdbHeap*saveHeap = mEnv_Heap;
+
+    if (ownsHeap)
+    {
+#ifdef MORK_DEBUG_HEAP_STATS
+      printf("%d blocks remaining \n", ((orkinHeap *) saveHeap)->HeapBlockCount());
+      mork_u4* array = (mork_u4*) this;
+      array -= 3;
+      // null out heap ptr in mem block so we won't crash trying to use it to
+      // delete the env.
+      *array = nsnull;
+#endif // MORK_DEBUG_HEAP_STATS
+      // whoops, this is our heap - hmm. Can't delete it, or not allocate env's from
+      // an orkinHeap.
+      delete saveHeap;
+    }
+
+  }
+//  MORK_ASSERT(mEnv_SelfAsMdbEnv==0);
+  MORK_ASSERT(mEnv_ErrorHook==0);
+}
+
+/* choose morkBool_kTrue or morkBool_kFalse for kBeVerbose: */
+#define morkEnv_kBeVerbose morkBool_kFalse
+
+/*public non-poly*/
+morkEnv::morkEnv(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+  morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap)
+: morkObject(inUsage, ioHeap, morkColor_kNone)
+, mEnv_Factory( ioFactory )
+, mEnv_Heap( ioSlotHeap )
+
+, mEnv_SelfAsMdbEnv( 0 )
+, mEnv_ErrorHook( 0 )
+, mEnv_HandlePool( 0 )
+  
+, mEnv_ErrorCount( 0 ) 
+, mEnv_WarningCount( 0 ) 
+
+, mEnv_ErrorCode( 0 )
+  
+, mEnv_DoTrace( morkBool_kFalse )
+, mEnv_AutoClear( morkAble_kDisabled )
+, mEnv_ShouldAbort( morkBool_kFalse )
+, mEnv_BeVerbose( morkEnv_kBeVerbose )
+, mEnv_OwnsHeap ( morkBool_kFalse )
+{
+  MORK_ASSERT(ioSlotHeap && ioFactory );
+  if ( ioSlotHeap )
+  {
+    // mEnv_Heap is NOT refcounted:
+    // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, this, &mEnv_Heap);
+    
+    mEnv_HandlePool = new morkPool(morkUsage::kGlobal,
+      (nsIMdbHeap*) 0, ioSlotHeap);
+      
+    MORK_ASSERT(mEnv_HandlePool);
+    if ( mEnv_HandlePool && this->Good() )
+    {
+      mNode_Derived = morkDerived_kEnv;
+      mNode_Refs += morkEnv_kWeakRefCountEnvBonus;
+    }
+  }
+}
+
+/*public non-poly*/
+morkEnv::morkEnv(morkEnv* ev, /*i*/
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbEnv* inSelfAsMdbEnv,
+  morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mEnv_Factory( ioFactory )
+, mEnv_Heap( ioSlotHeap )
+
+, mEnv_SelfAsMdbEnv( inSelfAsMdbEnv )
+, mEnv_ErrorHook( 0 )
+, mEnv_HandlePool( 0 )
+  
+, mEnv_ErrorCount( 0 ) 
+, mEnv_WarningCount( 0 ) 
+
+, mEnv_ErrorCode( 0 )
+  
+, mEnv_DoTrace( morkBool_kFalse )
+, mEnv_AutoClear( morkAble_kDisabled )
+, mEnv_ShouldAbort( morkBool_kFalse )
+, mEnv_BeVerbose( morkEnv_kBeVerbose )
+, mEnv_OwnsHeap ( morkBool_kFalse )
+{
+  // $$$ do we need to refcount the inSelfAsMdbEnv nsIMdbEnv??
+  
+  if ( ioFactory && inSelfAsMdbEnv && ioSlotHeap)
+  {
+    // mEnv_Heap is NOT refcounted:
+    // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mEnv_Heap);
+
+    mEnv_HandlePool = new(*ioSlotHeap, ev) morkPool(ev, 
+      morkUsage::kHeap, ioSlotHeap, ioSlotHeap);
+      
+    MORK_ASSERT(mEnv_HandlePool);
+    if ( mEnv_HandlePool && ev->Good() )
+    {
+      mNode_Derived = morkDerived_kEnv;
+      mNode_Refs += morkEnv_kWeakRefCountEnvBonus;
+    }
+  }
+  else
+    ev->NilPointerError();
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkEnv, morkObject, nsIMdbEnv)
+/*public non-poly*/ void
+morkEnv::CloseEnv(morkEnv* ev) /*i*/ // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      // $$$ release mEnv_SelfAsMdbEnv??
+      // $$$ release mEnv_ErrorHook??
+      
+      mEnv_SelfAsMdbEnv = 0;
+      mEnv_ErrorHook = 0;
+      
+      morkPool* savePool = mEnv_HandlePool;
+      morkPool::SlotStrongPool((morkPool*) 0, ev, &mEnv_HandlePool);
+      // free the pool
+      if (mEnv_SelfAsMdbEnv)
+      {
+        if (savePool && mEnv_Heap)
+          mEnv_Heap->Free(this->AsMdbEnv(), savePool);
+      }
+      else
+      {
+        if (savePool)
+        {
+          if (savePool->IsOpenNode())
+            savePool->CloseMorkNode(ev);
+          delete savePool;
+        }
+        // how do we free this? might need to get rid of asserts.
+      }
+      // mEnv_Factory is NOT refcounted
+      
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+mork_size
+morkEnv::OidAsHex(void* outBuf, const mdbOid& inOid)
+// sprintf(buf, "%lX:^%lX", (long) inOid.mOid_Id, (long) inOid.mOid_Scope);
+{
+  mork_u1* p = (mork_u1*) outBuf;
+  mork_size outSize = this->TokenAsHex(p, inOid.mOid_Id);
+  p += outSize;
+  *p++ = ':';
+  
+  mork_scope scope = inOid.mOid_Scope;
+  if ( scope < 0x80 && morkCh_IsName((mork_ch) scope) )
+  {
+    *p++ = (mork_u1) scope;
+    *p = 0; // null termination
+    outSize += 2;
+  }
+  else
+  {
+    *p++ = '^';
+    mork_size scopeSize = this->TokenAsHex(p, scope);
+    outSize += scopeSize + 2;
+  }
+  return outSize;
+}
+
+
+mork_u1
+morkEnv::HexToByte(mork_ch inFirstHex, mork_ch inSecondHex)
+{
+  mork_u1 hi = 0; // high four hex bits
+  mork_flags f = morkCh_GetFlags(inFirstHex);
+  if ( morkFlags_IsDigit(f) )
+    hi = (mork_u1) (inFirstHex - (mork_ch) '0');
+  else if ( morkFlags_IsUpper(f) )
+    hi = (mork_u1) ((inFirstHex - (mork_ch) 'A') + 10);
+  else if ( morkFlags_IsLower(f) )
+    hi = (mork_u1) ((inFirstHex - (mork_ch) 'a') + 10);
+  
+  mork_u1 lo = 0; // low four hex bits
+  f = morkCh_GetFlags(inSecondHex);
+  if ( morkFlags_IsDigit(f) )
+    lo = (mork_u1) (inSecondHex - (mork_ch) '0');
+  else if ( morkFlags_IsUpper(f) )
+    lo = (mork_u1) ((inSecondHex - (mork_ch) 'A') + 10);
+  else if ( morkFlags_IsLower(f) )
+    lo = (mork_u1) ((inSecondHex - (mork_ch) 'a') + 10);
+    
+  return (mork_u1) ((hi << 4) | lo);
+}
+
+mork_size
+morkEnv::TokenAsHex(void* outBuf, mork_token inToken)
+  // TokenAsHex() is the same as sprintf(outBuf, "%lX", (long) inToken);
+{
+  static const char morkEnv_kHexDigits[] = "0123456789ABCDEF";
+  char* p = (char*) outBuf;
+  char* end = p + 32; // write no more than 32 digits for safety
+  if ( inToken )
+  {
+    // first write all the hex digits in backwards order:
+    while ( p < end && inToken ) // more digits to write?
+    {
+      *p++ = morkEnv_kHexDigits[ inToken & 0x0F ]; // low four bits
+      inToken >>= 4; // we fervently hope this does not sign extend
+    }
+    *p = 0; // end the string with a null byte
+    char* s = (char*) outBuf; // first byte in string
+    mork_size size = (mork_size) (p - s); // distance from start
+
+    // now reverse the string in place:
+    // note that p starts on the null byte, so we need predecrement:
+    while ( --p > s ) // need to swap another byte in the string?
+    {
+      char c = *p; // temp for swap
+      *p = *s;
+      *s++ = c; // move s forward here, and p backward in the test
+    }
+    return size;
+  }
+  else // special case for zero integer
+  {
+    *p++ = '0'; // write a zero digit
+    *p = 0; // end with a null byte
+    return 1; // one digit in hex representation
+  }
+}
+
+void
+morkEnv::StringToYarn(const char* inString, mdbYarn* outYarn)
+{
+  if ( outYarn )
+  {
+    mdb_fill fill = ( inString )? (mdb_fill) MORK_STRLEN(inString) : 0; 
+      
+    if ( fill ) // have nonempty content?
+    {
+      mdb_size size = outYarn->mYarn_Size; // max dest size
+      if ( fill > size ) // too much string content?
+      {
+        outYarn->mYarn_More = fill - size; // extra string bytes omitted
+        fill = size; // copy no more bytes than size of yarn buffer
+      }
+      void* dest = outYarn->mYarn_Buf; // where bytes are going
+      if ( !dest ) // nil destination address buffer?
+        fill = 0; // we can't write any content at all
+        
+      if ( fill ) // anything to copy?
+        MORK_MEMCPY(dest, inString, fill); // copy fill bytes to yarn
+        
+      outYarn->mYarn_Fill = fill; // tell yarn size of copied content
+    }
+    else // no content to put into the yarn
+    {
+      outYarn->mYarn_Fill = 0; // tell yarn that string has no bytes
+    }
+    outYarn->mYarn_Form = 0; // always update the form slot
+  }
+  else
+    this->NilPointerError();
+}
+
+char*
+morkEnv::CopyString(nsIMdbHeap* ioHeap, const char* inString)
+{
+  char* outString = 0;
+  if ( ioHeap && inString )
+  {
+    mork_size size = MORK_STRLEN(inString) + 1;
+    ioHeap->Alloc(this->AsMdbEnv(), size, (void**) &outString);
+    if ( outString )
+      MORK_STRCPY(outString, inString);
+  }
+  else
+    this->NilPointerError();
+  return outString;
+}
+
+void
+morkEnv::FreeString(nsIMdbHeap* ioHeap, char* ioString)
+{
+  if ( ioHeap )
+  {
+    if ( ioString )
+      ioHeap->Free(this->AsMdbEnv(), ioString);
+  }
+  else
+    this->NilPointerError();
+}
+
+void
+morkEnv::NewErrorAndCode(const char* inString, mork_u2 inCode)
+{
+  MORK_ASSERT(morkBool_kFalse); // get developer's attention
+
+  ++mEnv_ErrorCount;
+  mEnv_ErrorCode = (mork_u4) ((inCode)? inCode: morkEnv_kGenericError);
+  
+  if ( mEnv_ErrorHook )
+    mEnv_ErrorHook->OnErrorString(this->AsMdbEnv(), inString);
+}
+
+void
+morkEnv::NewError(const char* inString)
+{
+  MORK_ASSERT(morkBool_kFalse); // get developer's attention
+
+  ++mEnv_ErrorCount;
+  mEnv_ErrorCode = morkEnv_kGenericError;
+  
+  if ( mEnv_ErrorHook )
+    mEnv_ErrorHook->OnErrorString(this->AsMdbEnv(), inString);
+}
+
+void
+morkEnv::NewWarning(const char* inString)
+{
+  MORK_ASSERT(morkBool_kFalse); // get developer's attention
+  
+  ++mEnv_WarningCount;
+  if ( mEnv_ErrorHook )
+    mEnv_ErrorHook->OnWarningString(this->AsMdbEnv(), inString);
+}
+
+void
+morkEnv::StubMethodOnlyError()
+{
+  this->NewError("method is stub only");
+}
+
+void
+morkEnv::OutOfMemoryError()
+{
+  this->NewError("out of memory");
+}
+
+void
+morkEnv::CantMakeWhenBadError()
+{
+  this->NewError("can't make an object when ev->Bad()");
+}
+
+static const char morkEnv_kNilPointer[] = "nil pointer";
+
+void
+morkEnv::NilPointerError()
+{
+  this->NewError(morkEnv_kNilPointer);
+}
+
+void
+morkEnv::NilPointerWarning()
+{
+  this->NewWarning(morkEnv_kNilPointer);
+}
+
+void
+morkEnv::NewNonEnvError()
+{
+  this->NewError("non-env instance");
+}
+
+void
+morkEnv::NilEnvSlotError()
+{
+  if ( !mEnv_HandlePool || !mEnv_Factory )
+  {
+    if ( !mEnv_HandlePool )
+      this->NewError("nil mEnv_HandlePool");
+    if ( !mEnv_Factory )
+      this->NewError("nil mEnv_Factory");
+  }
+  else
+    this->NewError("unknown nil env slot");
+}
+
+
+void morkEnv::NonEnvTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkEnv");
+}
+
+void
+morkEnv::ClearMorkErrorsAndWarnings()
+{
+  mEnv_ErrorCount = 0;
+  mEnv_WarningCount = 0;
+  mEnv_ErrorCode = 0;
+  mEnv_ShouldAbort = morkBool_kFalse;
+}
+
+void
+morkEnv::AutoClearMorkErrorsAndWarnings()
+{
+  if ( this->DoAutoClear() )
+  {
+    mEnv_ErrorCount = 0;
+    mEnv_WarningCount = 0;
+    mEnv_ErrorCode = 0;
+    mEnv_ShouldAbort = morkBool_kFalse;
+  }
+}
+
+/*static*/ morkEnv*
+morkEnv::FromMdbEnv(nsIMdbEnv* ioEnv) // dynamic type checking
+{
+  morkEnv* outEnv = 0;
+  if ( ioEnv )
+  {
+    // Note this cast is expected to perform some address adjustment of the
+    // pointer, so oenv likely does not equal ioEnv.  Do not cast to void*
+    // first to force an exactly equal pointer (we tried it and it's wrong).
+    morkEnv* ev = (morkEnv*) ioEnv;
+    if ( ev && ev->IsEnv() )
+    {
+      if ( ev->DoAutoClear() )
+      {
+        ev->mEnv_ErrorCount = 0;
+        ev->mEnv_WarningCount = 0;
+        ev->mEnv_ErrorCode = 0;
+      }
+      outEnv = ev;
+    }
+    else
+      MORK_ASSERT(outEnv);
+  }
+  else
+    MORK_ASSERT(outEnv);
+  return outEnv;
+}
+
+
+NS_IMETHODIMP
+morkEnv::GetErrorCount(mdb_count* outCount,
+  mdb_bool* outShouldAbort)
+{
+  if ( outCount )
+    *outCount = mEnv_ErrorCount;
+  if ( outShouldAbort )
+    *outShouldAbort = mEnv_ShouldAbort;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetWarningCount(mdb_count* outCount,
+  mdb_bool* outShouldAbort)
+{
+  if ( outCount )
+    *outCount = mEnv_WarningCount;
+  if ( outShouldAbort )
+    *outShouldAbort = mEnv_ShouldAbort;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetEnvBeVerbose(mdb_bool* outBeVerbose)
+{
+  NS_ENSURE_ARG_POINTER(outBeVerbose);
+  *outBeVerbose = mEnv_BeVerbose;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetEnvBeVerbose(mdb_bool inBeVerbose)
+{
+  mEnv_BeVerbose = inBeVerbose;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetDoTrace(mdb_bool* outDoTrace)
+{
+  NS_ENSURE_ARG_POINTER(outDoTrace);
+  *outDoTrace = mEnv_DoTrace;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetDoTrace(mdb_bool inDoTrace)
+{
+  mEnv_DoTrace = inDoTrace;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetAutoClear(mdb_bool* outAutoClear)
+{
+  NS_ENSURE_ARG_POINTER(outAutoClear);
+  *outAutoClear = DoAutoClear();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetAutoClear(mdb_bool inAutoClear)
+{
+  if ( inAutoClear )
+    EnableAutoClear();
+  else
+    DisableAutoClear();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetErrorHook(nsIMdbErrorHook** acqErrorHook)
+{
+  NS_ENSURE_ARG_POINTER(acqErrorHook);
+  *acqErrorHook = mEnv_ErrorHook;
+  NS_IF_ADDREF(mEnv_ErrorHook);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetErrorHook(
+  nsIMdbErrorHook* ioErrorHook) // becomes referenced
+{
+  mEnv_ErrorHook = ioErrorHook;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetHeap(nsIMdbHeap** acqHeap)
+{
+  NS_ENSURE_ARG_POINTER(acqHeap);
+  nsIMdbHeap* outHeap = 0;
+  nsIMdbHeap* heap = mEnv_Heap;
+  if ( heap && heap->HeapAddStrongRef(this) == 0 )
+    outHeap = heap;
+
+  if ( acqHeap )
+    *acqHeap = outHeap;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetHeap(
+  nsIMdbHeap* ioHeap) // becomes referenced
+{
+  nsIMdbHeap_SlotStrongHeap(ioHeap, this, &mEnv_Heap);
+  return NS_OK;
+}
+// } ----- end attribute methods -----
+
+NS_IMETHODIMP
+morkEnv::ClearErrors() // clear errors beore re-entering db API
+{
+  mEnv_ErrorCount = 0;
+  mEnv_ErrorCode = 0;
+  mEnv_ShouldAbort = morkBool_kFalse;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::ClearWarnings() // clear warning
+{
+  mEnv_WarningCount = 0;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::ClearErrorsAndWarnings() // clear both errors & warnings
+{
+  ClearMorkErrorsAndWarnings();
+  return NS_OK;
+}
+// } ===== end nsIMdbEnv methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkEnv.h
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKENV_
+#define _MORKENV_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+// sean was here
+#include "nsError.h"
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kEnv     /*i*/ 0x4576 /* ascii 'Ev' */
+
+// use NS error codes to make Mork easier to use with the rest of mozilla 
+#define morkEnv_kNoError         NS_SUCCEEDED /* no error has happened */
+#define morkEnv_kGenericError    NS_ERROR_FAILURE /* non-specific error code */
+#define morkEnv_kNonEnvTypeError NS_ERROR_FAILURE /* morkEnv::IsEnv() is false */
+
+#define morkEnv_kStubMethodOnlyError NS_ERROR_NO_INTERFACE
+#define morkEnv_kOutOfMemoryError    NS_ERROR_OUT_OF_MEMORY
+#define morkEnv_kNilPointerError     NS_ERROR_NULL_POINTER
+#define morkEnv_kNewNonEnvError      NS_ERROR_FAILURE 
+#define morkEnv_kNilEnvSlotError     NS_ERROR_FAILURE
+
+#define morkEnv_kBadFactoryError     NS_ERROR_FACTORY_NOT_LOADED
+#define morkEnv_kBadFactoryEnvError  NS_ERROR_FACTORY_NOT_LOADED
+#define morkEnv_kBadEnvError         NS_ERROR_FAILURE
+
+#define morkEnv_kNonHandleTypeError  NS_ERROR_FAILURE
+#define morkEnv_kNonOpenNodeError    NS_ERROR_FAILURE 
+
+
+#define morkEnv_kWeakRefCountEnvBonus 0 /* try NOT to leak all env instances */
+
+/*| morkEnv:
+|*/
+class morkEnv : public morkObject, public nsIMdbEnv {
+  NS_DECL_ISUPPORTS_INHERITED
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+  // mork_color   mBead_Color;   // ID for this bead
+  // morkHandle*  mObject_Handle;  // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+  
+  morkFactory*      mEnv_Factory;  // NON-refcounted factory
+  nsIMdbHeap*       mEnv_Heap;     // NON-refcounted heap
+
+  nsIMdbEnv*        mEnv_SelfAsMdbEnv;
+  nsIMdbErrorHook*  mEnv_ErrorHook;
+  
+  morkPool*         mEnv_HandlePool; // pool for re-using handles
+    
+  mork_u2           mEnv_ErrorCount; 
+  mork_u2           mEnv_WarningCount; 
+  
+  mork_u4           mEnv_ErrorCode; // simple basis for mdb_err style errors
+  
+  mork_bool         mEnv_DoTrace;
+  mork_able         mEnv_AutoClear;
+  mork_bool         mEnv_ShouldAbort;
+  mork_bool         mEnv_BeVerbose;
+  mork_bool         mEnv_OwnsHeap;
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseEnv() only if open
+  virtual ~morkEnv(); // assert that CloseEnv() executed earlier
+  
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetErrorCount(mdb_count* outCount,
+    mdb_bool* outShouldAbort);
+  NS_IMETHOD GetWarningCount(mdb_count* outCount,
+    mdb_bool* outShouldAbort);
+  
+  NS_IMETHOD GetEnvBeVerbose(mdb_bool* outBeVerbose);
+  NS_IMETHOD SetEnvBeVerbose(mdb_bool inBeVerbose);
+  
+  NS_IMETHOD GetDoTrace(mdb_bool* outDoTrace);
+  NS_IMETHOD SetDoTrace(mdb_bool inDoTrace);
+  
+  NS_IMETHOD GetAutoClear(mdb_bool* outAutoClear);
+  NS_IMETHOD SetAutoClear(mdb_bool inAutoClear);
+  
+  NS_IMETHOD GetErrorHook(nsIMdbErrorHook** acqErrorHook);
+  NS_IMETHOD SetErrorHook(
+    nsIMdbErrorHook* ioErrorHook); // becomes referenced
+  
+  NS_IMETHOD GetHeap(nsIMdbHeap** acqHeap);
+  NS_IMETHOD SetHeap(
+    nsIMdbHeap* ioHeap); // becomes referenced
+  // } ----- end attribute methods -----
+  
+  NS_IMETHOD ClearErrors(); // clear errors beore re-entering db API
+  NS_IMETHOD ClearWarnings(); // clear warnings
+  NS_IMETHOD ClearErrorsAndWarnings(); // clear both errors & warnings
+// } ===== end nsIMdbEnv methods =====
+public: // morkEnv construction & destruction
+  morkEnv(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+    morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap);
+  morkEnv(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+     nsIMdbEnv* inSelfAsMdbEnv, morkFactory* ioFactory,
+     nsIMdbHeap* ioSlotHeap);
+  void CloseEnv(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkEnv(const morkEnv& other);
+  morkEnv& operator=(const morkEnv& other);
+
+public: // dynamic type identification
+  mork_bool IsEnv() const
+  { return IsNode() && mNode_Derived == morkDerived_kEnv; }
+// } ===== end morkNode methods =====
+
+public: // utility env methods
+
+  mork_u1 HexToByte(mork_ch inFirstHex, mork_ch inSecondHex);
+
+  mork_size TokenAsHex(void* outBuf, mork_token inToken);
+  // TokenAsHex() is the same as sprintf(outBuf, "%lX", (long) inToken);
+ 
+  mork_size OidAsHex(void* outBuf, const mdbOid& inOid);
+  // sprintf(buf, "%lX:^%lX", (long) inOid.mOid_Id, (long) inOid.mOid_Scope);
+ 
+  char* CopyString(nsIMdbHeap* ioHeap, const char* inString);
+  void  FreeString(nsIMdbHeap* ioHeap, char* ioString);
+  void  StringToYarn(const char* inString, mdbYarn* outYarn);
+
+public: // other env methods
+
+  morkHandleFace*  NewHandle(mork_size inSize)
+  { return mEnv_HandlePool->NewHandle(this, inSize, (morkZone*) 0); }
+  
+  void ZapHandle(morkHandleFace* ioHandle)
+  { mEnv_HandlePool->ZapHandle(this, ioHandle); }
+
+  void EnableAutoClear() { mEnv_AutoClear = morkAble_kEnabled; }
+  void DisableAutoClear() { mEnv_AutoClear = morkAble_kDisabled; }
+  
+  mork_bool DoAutoClear() const
+  { return mEnv_AutoClear == morkAble_kEnabled; }
+
+  void NewErrorAndCode(const char* inString, mork_u2 inCode);
+  void NewError(const char* inString);
+  void NewWarning(const char* inString);
+
+  void ClearMorkErrorsAndWarnings(); // clear both errors & warnings
+  void AutoClearMorkErrorsAndWarnings(); // clear if auto is enabled
+  
+  void StubMethodOnlyError();
+  void OutOfMemoryError();
+  void NilPointerError();
+  void NilPointerWarning();
+  void CantMakeWhenBadError();
+  void NewNonEnvError();
+  void NilEnvSlotError();
+    
+  void NonEnvTypeError(morkEnv* ev);
+  
+  // canonical env convenience methods to check for presence of errors:
+  mork_bool Good() const { return ( mEnv_ErrorCount == 0 ); }
+  mork_bool Bad() const { return ( mEnv_ErrorCount != 0 ); }
+  
+  nsIMdbEnv* AsMdbEnv() { return (nsIMdbEnv *) this; }
+  static morkEnv* FromMdbEnv(nsIMdbEnv* ioEnv); // dynamic type checking
+  
+  mork_u4 ErrorCode() const { return mEnv_ErrorCode; }
+  
+  mdb_err AsErr() const { return (mdb_err) mEnv_ErrorCode; }
+  //mdb_err AsErr() const { return (mdb_err) ( mEnv_ErrorCount != 0 ); }
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakEnv(morkEnv* me,
+    morkEnv* ev, morkEnv** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongEnv(morkEnv* me,
+    morkEnv* ev, morkEnv** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKENV_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkFactory.cpp
@@ -0,0 +1,656 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKFACTORY_
+#include "morkFactory.h"
+#endif
+
+#ifndef _ORKINHEAP_
+#include "orkinHeap.h"
+#endif
+
+#ifndef _MORKFILE_
+#include "morkFile.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTHUMB_
+#include "morkThumb.h"
+#endif
+
+#ifndef _MORKWRITER_
+#include "morkWriter.h"
+#endif
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkFactory::CloseMorkNode(morkEnv* ev) /*i*/ // CloseFactory() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseFactory(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkFactory::~morkFactory() /*i*/ // assert CloseFactory() executed earlier
+{
+  CloseFactory(&mFactory_Env);
+  MORK_ASSERT(mFactory_Env.IsShutNode());
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkFactory::morkFactory() // uses orkinHeap
+: morkObject(morkUsage::kGlobal, (nsIMdbHeap*) 0, morkColor_kNone)
+, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this,
+  new orkinHeap())
+, mFactory_Heap()
+{
+  if ( mFactory_Env.Good() )
+  {
+    mNode_Derived = morkDerived_kFactory;
+    mNode_Refs += morkFactory_kWeakRefCountBonus;
+  }
+}
+
+/*public non-poly*/
+morkFactory::morkFactory(nsIMdbHeap* ioHeap)
+: morkObject(morkUsage::kHeap, ioHeap, morkColor_kNone)
+, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this, ioHeap)
+, mFactory_Heap()
+{
+  if ( mFactory_Env.Good() )
+  {
+    mNode_Derived = morkDerived_kFactory;
+    mNode_Refs += morkFactory_kWeakRefCountBonus;
+  }
+}
+
+/*public non-poly*/
+morkFactory::morkFactory(morkEnv* ev, /*i*/
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this, ioHeap)
+, mFactory_Heap()
+{
+  if ( ev->Good() )
+  {
+    mNode_Derived = morkDerived_kFactory;
+    mNode_Refs += morkFactory_kWeakRefCountBonus;
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkFactory, morkObject, nsIMdbFactory)
+
+extern "C" nsIMdbFactory* MakeMdbFactory() 
+{
+  return new morkFactory(new orkinHeap());
+}
+
+
+/*public non-poly*/ void
+morkFactory::CloseFactory(morkEnv* ev) /*i*/ // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mFactory_Env.CloseMorkNode(ev);
+      this->CloseObject(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+morkEnv* morkFactory::GetInternalFactoryEnv(mdb_err* outErr)
+{
+  morkEnv* outEnv = 0;
+  if (IsNode() && IsOpenNode() && IsFactory() )
+  {
+    morkEnv* fenv = &mFactory_Env;
+    if ( fenv && fenv->IsNode() && fenv->IsOpenNode() && fenv->IsEnv() )
+    {
+      fenv->ClearMorkErrorsAndWarnings(); // drop any earlier errors
+      outEnv = fenv;
+    }
+    else
+      *outErr = morkEnv_kBadFactoryEnvError;
+  }
+  else
+    *outErr = morkEnv_kBadFactoryError;
+    
+  return outEnv;
+}
+
+
+void
+morkFactory::NonFactoryTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkFactory");
+}
+
+
+NS_IMETHODIMP
+morkFactory::OpenOldFile(nsIMdbEnv* mev, nsIMdbHeap* ioHeap,
+  const char* inFilePath,
+  mork_bool inFrozen, nsIMdbFile** acqFile)
+  // Choose some subclass of nsIMdbFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be open and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  morkFile* file = nsnull;
+  if ( ev )
+  {
+    if ( !ioHeap )
+      ioHeap = &mFactory_Heap;
+      
+    file = morkFile::OpenOldFile(ev, ioHeap, inFilePath, inFrozen);
+    NS_IF_ADDREF( file );
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqFile )
+    *acqFile = file;
+    
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkFactory::CreateNewFile(nsIMdbEnv* mev, nsIMdbHeap* ioHeap,
+  const char* inFilePath, nsIMdbFile** acqFile)
+  // Choose some subclass of nsIMdbFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be created and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  morkFile* file = nsnull;
+  if ( ev )
+  {
+    if ( !ioHeap )
+      ioHeap = &mFactory_Heap;
+      
+    file = morkFile::CreateNewFile(ev, ioHeap, inFilePath);
+    if ( file )
+      NS_ADDREF(file);
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqFile )
+    *acqFile = file;
+    
+  return outErr;
+}
+// } ----- end file methods -----
+
+// { ----- begin env methods -----
+NS_IMETHODIMP
+morkFactory::MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv)
+// ioHeap can be nil, causing a MakeHeap() style heap instance to be used
+{
+  mdb_err outErr = 0;
+  nsIMdbEnv* outEnv = 0;
+  mork_bool ownsHeap = (ioHeap == 0);
+  if ( !ioHeap )
+    ioHeap = new orkinHeap();
+
+  if ( acqEnv && ioHeap )
+  {
+    morkEnv* fenv = this->GetInternalFactoryEnv(&outErr);
+    if ( fenv )
+    {
+      morkEnv* newEnv = new(*ioHeap, fenv)
+        morkEnv(morkUsage::kHeap, ioHeap, this, ioHeap);
+
+      if ( newEnv )
+      {
+        newEnv->mEnv_OwnsHeap = ownsHeap;
+        newEnv->mNode_Refs += morkEnv_kWeakRefCountEnvBonus;
+        NS_ADDREF(newEnv);
+        newEnv->mEnv_SelfAsMdbEnv = newEnv;
+        outEnv = newEnv;
+      }
+      else
+        outErr = morkEnv_kOutOfMemoryError;
+    }
+    
+    *acqEnv = outEnv;
+  }
+  else
+    outErr = morkEnv_kNilPointerError;
+    
+  return outErr;
+}
+// } ----- end env methods -----
+
+// { ----- begin heap methods -----
+NS_IMETHODIMP
+morkFactory::MakeHeap(nsIMdbEnv* mev, nsIMdbHeap** acqHeap)
+{
+  mdb_err outErr = 0;
+  nsIMdbHeap* outHeap = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    outHeap = new orkinHeap();
+    if ( !outHeap )
+      ev->OutOfMemoryError();
+  }
+  MORK_ASSERT(acqHeap);
+  if ( acqHeap )
+    *acqHeap = outHeap;
+  return outErr;
+}
+// } ----- end heap methods -----
+
+// { ----- begin compare methods -----
+NS_IMETHODIMP
+morkFactory::MakeCompare(nsIMdbEnv* mev, nsIMdbCompare** acqCompare)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end compare methods -----
+
+// { ----- begin row methods -----
+NS_IMETHODIMP
+morkFactory::MakeRow(nsIMdbEnv* mev, nsIMdbHeap* ioHeap,
+  nsIMdbRow** acqRow)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// ioHeap can be nil, causing the heap associated with ev to be used
+// } ----- end row methods -----
+
+// { ----- begin port methods -----
+NS_IMETHODIMP
+morkFactory::CanOpenFilePort(
+  nsIMdbEnv* mev, // context
+  // const char* inFilePath, // the file to investigate
+  // const mdbYarn* inFirst512Bytes,
+  nsIMdbFile* ioFile, // db abstract file interface
+  mdb_bool* outCanOpen, // whether OpenFilePort() might succeed
+  mdbYarn* outFormatVersion)
+{
+  mdb_err outErr = 0;
+  if ( outFormatVersion )
+  {
+    outFormatVersion->mYarn_Fill = 0;
+  }
+  mdb_bool canOpenAsPort = morkBool_kFalse;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( ioFile && outCanOpen )
+    {
+      canOpenAsPort = this->CanOpenMorkTextFile(ev, ioFile);
+    }
+    else
+      ev->NilPointerError();
+    
+    outErr = ev->AsErr();
+  }
+    
+  if ( outCanOpen )
+    *outCanOpen = canOpenAsPort;
+    
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkFactory::OpenFilePort(
+  nsIMdbEnv* mev, // context
+  nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+  // const char* inFilePath, // the file to open for readonly import
+  nsIMdbFile* ioFile, // db abstract file interface
+  const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+  nsIMdbThumb** acqThumb)
+{
+  NS_ASSERTION(PR_FALSE, "this doesn't look implemented");
+  MORK_USED_1(ioHeap);
+  mdb_err outErr = 0;
+  nsIMdbThumb* outThumb = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+   if ( ev )
+  {
+    if ( ioFile && inOpenPolicy && acqThumb )
+    {
+    }
+    else
+      ev->NilPointerError();
+    
+    outErr = ev->AsErr();
+  }
+  if ( acqThumb )
+    *acqThumb = outThumb;
+  return outErr;
+}
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then call nsIMdbFactory::ThumbToOpenPort() to get the port instance.
+
+NS_IMETHODIMP
+morkFactory::ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort()
+  nsIMdbEnv* mev, // context
+  nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status
+  nsIMdbPort** acqPort)
+{
+  mdb_err outErr = 0;
+  nsIMdbPort* outPort = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( ioThumb && acqPort )
+    {
+      morkThumb* thumb = (morkThumb*) ioThumb;
+      morkStore* store = thumb->ThumbToOpenStore(ev);
+      if ( store )
+      {
+        store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue;
+        store->mStore_CanDirty = morkBool_kTrue;
+        store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue);
+        
+        NS_ADDREF(store);
+        outPort = store;
+      }
+    }
+    else
+      ev->NilPointerError();
+    
+    outErr = ev->AsErr();
+  }
+  if ( acqPort )
+    *acqPort = outPort;
+  return outErr;
+}
+// } ----- end port methods -----
+
+mork_bool
+morkFactory::CanOpenMorkTextFile(morkEnv* ev,
+  // const mdbYarn* inFirst512Bytes,
+  nsIMdbFile* ioFile)
+{
+  MORK_USED_1(ev);
+  mork_bool outBool = morkBool_kFalse;
+  mork_size headSize = MORK_STRLEN(morkWriter_kFileHeader);
+  
+  char localBuf[ 256 + 4 ]; // for extra for sloppy safety
+  mdbYarn localYarn;
+  mdbYarn* y = &localYarn;
+  y->mYarn_Buf = localBuf; // space to hold content
+  y->mYarn_Fill = 0;       // no logical content yet
+  y->mYarn_Size = 256;     // physical capacity is 256 bytes
+  y->mYarn_More = 0;
+  y->mYarn_Form = 0;
+  y->mYarn_Grow = 0;
+  
+  if ( ioFile )
+  {
+    nsIMdbEnv* menv = ev->AsMdbEnv();
+    mdb_size actualSize = 0;
+    ioFile->Get(menv, y->mYarn_Buf, y->mYarn_Size, /*pos*/ 0, &actualSize);
+    y->mYarn_Fill = actualSize;
+    
+    if ( y->mYarn_Buf && actualSize >= headSize && ev->Good() )
+    {
+      mork_u1* buf = (mork_u1*) y->mYarn_Buf;
+      outBool = ( MORK_MEMCMP(morkWriter_kFileHeader, buf, headSize) == 0 );
+    }
+  }
+  else
+    ev->NilPointerError();
+
+  return outBool;
+}
+
+// { ----- begin store methods -----
+NS_IMETHODIMP
+morkFactory::CanOpenFileStore(
+  nsIMdbEnv* mev, // context
+  // const char* inFilePath, // the file to investigate
+  // const mdbYarn* inFirst512Bytes,
+  nsIMdbFile* ioFile, // db abstract file interface
+  mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed
+  mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed
+  mdbYarn* outFormatVersion)
+{
+  mdb_bool canOpenAsStore = morkBool_kFalse;
+  mdb_bool canOpenAsPort = morkBool_kFalse;
+  if ( outFormatVersion )
+  {
+    outFormatVersion->mYarn_Fill = 0;
+  }
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( ioFile && outCanOpenAsStore )
+    {
+      // right now always say true; later we should look for magic patterns
+      canOpenAsStore = this->CanOpenMorkTextFile(ev, ioFile);
+      canOpenAsPort = canOpenAsStore;
+    }
+    else
+      ev->NilPointerError();
+    
+    outErr = ev->AsErr();
+  }
+  if ( outCanOpenAsStore )
+    *outCanOpenAsStore = canOpenAsStore;
+    
+  if ( outCanOpenAsPort )
+    *outCanOpenAsPort = canOpenAsPort;
+    
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkFactory::OpenFileStore( // open an existing database
+  nsIMdbEnv* mev, // context
+  nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+  // const char* inFilePath, // the file to open for general db usage
+  nsIMdbFile* ioFile, // db abstract file interface
+  const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+  nsIMdbThumb** acqThumb)
+{
+  mdb_err outErr = 0;
+  nsIMdbThumb* outThumb = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( !ioHeap ) // need to use heap from env?
+      ioHeap = ev->mEnv_Heap;
+    
+    if ( ioFile && inOpenPolicy && acqThumb )
+    {
+      morkStore* store = new(*ioHeap, ev)
+        morkStore(ev, morkUsage::kHeap, ioHeap, this, ioHeap);
+        
+      if ( store )
+      {
+        mork_bool frozen = morkBool_kFalse; // open store mutable access
+        if ( store->OpenStoreFile(ev, frozen, ioFile, inOpenPolicy) )
+        {
+          morkThumb* thumb = morkThumb::Make_OpenFileStore(ev, ioHeap, store);
+          if ( thumb )
+          {
+            outThumb = thumb;
+            thumb->AddRef();
+          }
+        }
+//        store->CutStrongRef(mev); // always cut ref (handle has its own ref)
+      }
+    }
+    else
+      ev->NilPointerError();
+    
+    outErr = ev->AsErr();
+  }
+  if ( acqThumb )
+    *acqThumb = outThumb;
+  return outErr;
+}
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then call nsIMdbFactory::ThumbToOpenStore() to get the store instance.
+  
+NS_IMETHODIMP
+morkFactory::ThumbToOpenStore( // redeem completed thumb from OpenFileStore()
+  nsIMdbEnv* mev, // context
+  nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status
+  nsIMdbStore** acqStore)
+{
+  mdb_err outErr = 0;
+  nsIMdbStore* outStore = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( ioThumb && acqStore )
+    {
+      morkThumb* thumb = (morkThumb*) ioThumb;
+      morkStore* store = thumb->ThumbToOpenStore(ev);
+      if ( store )
+      {
+        store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue;
+        store->mStore_CanDirty = morkBool_kTrue;
+        store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue);
+        
+        outStore = store;
+        NS_ADDREF(store);
+      }
+    }
+    else
+      ev->NilPointerError();
+    
+    outErr = ev->AsErr();
+  }
+  if ( acqStore )
+    *acqStore = outStore;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkFactory::CreateNewFileStore( // create a new db with minimal content
+  nsIMdbEnv* mev, // context
+  nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+  // const char* inFilePath, // name of file which should not yet exist
+  nsIMdbFile* ioFile, // db abstract file interface
+  const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+  nsIMdbStore** acqStore)
+{
+  mdb_err outErr = 0;
+  nsIMdbStore* outStore = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( !ioHeap ) // need to use heap from env?
+      ioHeap = ev->mEnv_Heap;
+    
+    if ( ioFile && inOpenPolicy && acqStore && ioHeap )
+    {
+      morkStore* store = new(*ioHeap, ev)
+        morkStore(ev, morkUsage::kHeap, ioHeap, this, ioHeap);
+        
+      if ( store )
+      {
+        store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue;
+        store->mStore_CanDirty = morkBool_kTrue;
+        store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue);
+
+        if ( store->CreateStoreFile(ev, ioFile, inOpenPolicy) )
+          outStore = store;
+        NS_ADDREF(store);          
+      }
+    }
+    else
+      ev->NilPointerError();
+    
+    outErr = ev->AsErr();
+  }
+  if ( acqStore )
+    *acqStore = outStore;
+  return outErr;
+}
+// } ----- end store methods -----
+
+// } ===== end nsIMdbFactory methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkFactory.h
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKFACTORY_
+#define _MORKFACTORY_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _ORKINHEAP_
+#include "orkinHeap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class nsIMdbFactory;
+
+#define morkDerived_kFactory  /*i*/ 0x4663 /* ascii 'Fc' */
+#define morkFactory_kWeakRefCountBonus 0 /* try NOT to leak all factories */
+
+/*| morkFactory: 
+|*/
+class morkFactory : public morkObject, public nsIMdbFactory { // nsIMdbObject
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+  // mork_color   mBead_Color;   // ID for this bead
+  // morkHandle*  mObject_Handle;  // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+
+  morkEnv        mFactory_Env; // private env instance used internally
+  orkinHeap      mFactory_Heap;
+
+  NS_DECL_ISUPPORTS_INHERITED
+// { ===== begin morkNode interface =====
+public: // morkFactory virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseFactory() only if open
+  virtual ~morkFactory(); // assert that CloseFactory() executed earlier
+
+
+// { ===== begin nsIMdbFactory methods =====
+
+  // { ----- begin file methods -----
+  NS_IMETHOD OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+    const char* inFilePath,
+    mdb_bool inFrozen, nsIMdbFile** acqFile);
+  // Choose some subclass of nsIMdbFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be open and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+
+  NS_IMETHOD CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+    const char* inFilePath,
+    nsIMdbFile** acqFile);
+  // Choose some subclass of nsIMdbFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be created and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+  // } ----- end file methods -----
+
+  // { ----- begin env methods -----
+  NS_IMETHOD MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv); // new env
+  // ioHeap can be nil, causing a MakeHeap() style heap instance to be used
+  // } ----- end env methods -----
+
+  // { ----- begin heap methods -----
+  NS_IMETHOD MakeHeap(nsIMdbEnv* ev, nsIMdbHeap** acqHeap); // new heap
+  // } ----- end heap methods -----
+
+  // { ----- begin compare methods -----
+  NS_IMETHOD MakeCompare(nsIMdbEnv* ev, nsIMdbCompare** acqCompare); // ASCII
+  // } ----- end compare methods -----
+
+  // { ----- begin row methods -----
+  NS_IMETHOD MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbRow** acqRow); // new row
+  // ioHeap can be nil, causing the heap associated with ev to be used
+  // } ----- end row methods -----
+  
+  // { ----- begin port methods -----
+  NS_IMETHOD CanOpenFilePort(
+    nsIMdbEnv* ev, // context
+    // const char* inFilePath, // the file to investigate
+    // const mdbYarn* inFirst512Bytes,
+    nsIMdbFile* ioFile, // db abstract file interface
+    mdb_bool* outCanOpen, // whether OpenFilePort() might succeed
+    mdbYarn* outFormatVersion); // informal file format description
+    
+  NS_IMETHOD OpenFilePort(
+    nsIMdbEnv* ev, // context
+    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+    // const char* inFilePath, // the file to open for readonly import
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental port open
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance.
+
+  NS_IMETHOD ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort()
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status
+    nsIMdbPort** acqPort); // acquire new port object
+  // } ----- end port methods -----
+  
+  // { ----- begin store methods -----
+  NS_IMETHOD CanOpenFileStore(
+    nsIMdbEnv* ev, // context
+    // const char* inFilePath, // the file to investigate
+    // const mdbYarn* inFirst512Bytes,
+    nsIMdbFile* ioFile, // db abstract file interface
+    mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed
+    mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed
+    mdbYarn* outFormatVersion); // informal file format description
+    
+  NS_IMETHOD OpenFileStore( // open an existing database
+    nsIMdbEnv* ev, // context
+    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+    // const char* inFilePath, // the file to open for general db usage
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental store open
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance.
+    
+  NS_IMETHOD
+  ThumbToOpenStore( // redeem completed thumb from OpenFileStore()
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status
+    nsIMdbStore** acqStore); // acquire new db store object
+  
+  NS_IMETHOD CreateNewFileStore( // create a new db with minimal content
+    nsIMdbEnv* ev, // context
+    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+    // const char* inFilePath, // name of file which should not yet exist
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+    nsIMdbStore** acqStore); // acquire new db store object
+  // } ----- end store methods -----
+
+// } ===== end nsIMdbFactory methods =====
+  
+public: // morkYarn construction & destruction
+  morkFactory(); // uses orkinHeap
+  morkFactory(nsIMdbHeap* ioHeap); // caller supplied heap
+  morkFactory(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+  void CloseFactory(morkEnv* ev); // called by CloseMorkNode();
+  
+  
+public: // morkNode memory management operators
+  void* operator new(size_t inSize) CPP_THROW_NEW
+  { return ::operator new(inSize); }
+  
+  void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+  { return morkNode::MakeNew(inSize, ioHeap, ev); }
+  
+private: // copying is not allowed
+  morkFactory(const morkFactory& other);
+  morkFactory& operator=(const morkFactory& other);
+
+public: // dynamic type identification
+  mork_bool IsFactory() const
+  { return IsNode() && mNode_Derived == morkDerived_kFactory; }
+// } ===== end morkNode methods =====
+
+public: // other factory methods
+
+  void NonFactoryTypeError(morkEnv* ev);
+  morkEnv* GetInternalFactoryEnv(mdb_err* outErr);
+  mork_bool CanOpenMorkTextFile(morkEnv* ev, nsIMdbFile* ioFile);
+  
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakFactory(morkFactory* me,
+    morkEnv* ev, morkFactory** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongFactory(morkFactory* me,
+    morkEnv* ev, morkFactory** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKFACTORY_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkFile.cpp
@@ -0,0 +1,926 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKFILE_
+#include "morkFile.h"
+#endif
+
+#ifdef MORK_WIN
+#include "io.h"
+#include <windows.h>
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkFile::CloseMorkNode(morkEnv* ev) // CloseFile() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseFile(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkFile::~morkFile() // assert CloseFile() executed earlier
+{
+  MORK_ASSERT(mFile_Frozen==0);
+  MORK_ASSERT(mFile_DoTrace==0);
+  MORK_ASSERT(mFile_IoOpen==0);
+  MORK_ASSERT(mFile_Active==0);
+}
+
+/*public non-poly*/
+morkFile::morkFile(morkEnv* ev, const morkUsage& inUsage, 
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mFile_Frozen( 0 )
+, mFile_DoTrace( 0 )
+, mFile_IoOpen( 0 )
+, mFile_Active( 0 )
+
+, mFile_SlotHeap( 0 )
+, mFile_Name( 0 )
+, mFile_Thief( 0 )
+{
+  if ( ev->Good() )
+  {
+    if ( ioSlotHeap )
+    {
+      nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mFile_SlotHeap);
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kFile;
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkFile, morkObject, nsIMdbFile)
+/*public non-poly*/ void
+morkFile::CloseFile(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mFile_Frozen = 0;
+      mFile_DoTrace = 0;
+      mFile_IoOpen = 0;
+      mFile_Active = 0;
+      
+      if ( mFile_Name )
+        this->SetFileName(ev, (const char*) 0);
+
+      nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mFile_SlotHeap);
+      nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mFile_Thief);
+
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ morkFile*
+morkFile::OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+  const char* inFilePath, mork_bool inFrozen)
+  // Choose some subclass of morkFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be open and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+{
+  return morkStdioFile::OpenOldStdioFile(ev, ioHeap, inFilePath, inFrozen);
+}
+
+/*static*/ morkFile*
+morkFile::CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+  const char* inFilePath)
+  // Choose some subclass of morkFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be created and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+{
+  return morkStdioFile::CreateNewStdioFile(ev, ioHeap, inFilePath);
+}
+
+void
+morkFile::NewMissingIoError(morkEnv* ev) const
+{
+  ev->NewError("file missing io");
+}
+
+/*static*/ void
+morkFile::NonFileTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkFile");
+}
+
+/*static*/ void
+morkFile::NilSlotHeapError(morkEnv* ev)
+{
+  ev->NewError("nil mFile_SlotHeap");
+}
+
+/*static*/ void
+morkFile::NilFileNameError(morkEnv* ev)
+{
+  ev->NewError("nil mFile_Name");
+}
+
+void
+morkFile::SetThief(morkEnv* ev, nsIMdbFile* ioThief)
+{
+  nsIMdbFile_SlotStrongFile(ioThief, ev, &mFile_Thief);
+}
+
+void
+morkFile::SetFileName(morkEnv* ev, const char* inName) // inName can be nil
+{
+  nsIMdbHeap* heap = mFile_SlotHeap;
+  if ( heap )
+  {
+    char* name = mFile_Name;
+    if ( name )
+    {
+      mFile_Name = 0;
+      ev->FreeString(heap, name);
+    }
+    if ( ev->Good() && inName )
+      mFile_Name = ev->CopyString(heap, inName);
+  }
+  else
+    this->NilSlotHeapError(ev);
+}
+
+void
+morkFile::NewFileDownError(morkEnv* ev) const
+// call NewFileDownError() when either IsOpenAndActiveFile()
+// is false, or when IsOpenActiveAndMutableFile() is false.
+{
+  if ( this->IsOpenNode() )
+  {
+    if ( this->FileActive() )
+    {
+      if ( this->FileFrozen() )
+      {
+        ev->NewError("file frozen");
+      }
+      else
+        ev->NewError("unknown file problem");
+    }
+    else
+      ev->NewError("file not active");
+  }
+  else
+    ev->NewError("file not open");
+}
+
+void
+morkFile::NewFileErrnoError(morkEnv* ev) const
+// call NewFileErrnoError() to convert std C errno into AB fault
+{
+  const char* errnoString = strerror(errno);
+  ev->NewError(errnoString); // maybe pass value of strerror() instead
+}
+
+// ````` ````` ````` ````` newlines ````` ````` ````` `````  
+
+#if defined(MORK_MAC)
+       static const char morkFile_kNewlines[] = 
+       "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015";
+#      define morkFile_kNewlinesCount 16
+#else
+#  if defined(MORK_WIN) || defined(MORK_OS2)
+       static const char morkFile_kNewlines[] = 
+       "\015\012\015\012\015\012\015\012\015\012\015\012\015\012\015\012";
+#    define morkFile_kNewlinesCount 8
+#  else
+#    ifdef MORK_UNIX
+       static const char morkFile_kNewlines[] = 
+       "\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012";
+#      define morkFile_kNewlinesCount 16
+#    endif /* MORK_UNIX */
+#  endif /* MORK_WIN */
+#endif /* MORK_MAC */
+
+mork_size
+morkFile::WriteNewlines(morkEnv* ev, mork_count inNewlines)
+  // WriteNewlines() returns the number of bytes written.
+{
+  mork_size outSize = 0;
+  while ( inNewlines && ev->Good() ) // more newlines to write?
+  {
+    mork_u4 quantum = inNewlines;
+    if ( quantum > morkFile_kNewlinesCount )
+      quantum = morkFile_kNewlinesCount;
+
+    mork_size quantumSize = quantum * mork_kNewlineSize;
+    mdb_size bytesWritten;
+    this->Write(ev->AsMdbEnv(), morkFile_kNewlines, quantumSize, &bytesWritten);
+    outSize += quantumSize;
+    inNewlines -= quantum;
+  }
+  return outSize;
+}
+
+NS_IMETHODIMP
+morkFile::Eof(nsIMdbEnv* mev, mdb_pos* outPos)
+{
+  mdb_err outErr = 0;
+  mdb_pos pos = -1;
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  pos = Length(ev);
+  outErr = ev->AsErr();
+  if ( outPos )
+    *outPos = pos;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkFile::Get(nsIMdbEnv* mev, void* outBuf, mdb_size inSize,
+  mdb_pos inPos, mdb_size* outActualSize)
+{
+  nsresult rv = NS_OK;
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    mdb_pos outPos;
+    Seek(mev, inPos, &outPos);
+    if ( ev->Good() )
+      rv = Read(mev, outBuf, inSize, outActualSize);
+  }
+  return rv;
+}
+
+NS_IMETHODIMP
+morkFile::Put(nsIMdbEnv* mev, const void* inBuf, mdb_size inSize,
+  mdb_pos inPos, mdb_size* outActualSize)
+{
+  mdb_err outErr = 0;
+  *outActualSize = 0;
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    mdb_pos outPos;
+
+    Seek(mev, inPos, &outPos);
+    if ( ev->Good() )
+      Write(mev, inBuf, inSize, outActualSize);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+// { ----- begin path methods -----
+NS_IMETHODIMP
+morkFile::Path(nsIMdbEnv* mev, mdbYarn* outFilePath)
+{
+  mdb_err outErr = 0;
+  if ( outFilePath )
+    outFilePath->mYarn_Fill = 0;
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    ev->StringToYarn(GetFileNameString(), outFilePath);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+// } ----- end path methods -----
+  
+// { ----- begin replacement methods -----
+
+
+NS_IMETHODIMP
+morkFile::Thief(nsIMdbEnv* mev, nsIMdbFile** acqThief)
+{
+  mdb_err outErr = 0;
+  nsIMdbFile* outThief = 0;
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    outThief = GetThief();
+    NS_IF_ADDREF(outThief);
+    outErr = ev->AsErr();
+  }
+  if ( acqThief )
+    *acqThief = outThief;
+  return outErr;
+}
+
+// } ----- end replacement methods -----
+
+// { ----- begin versioning methods -----
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkStdioFile::CloseMorkNode(morkEnv* ev) // CloseStdioFile() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseStdioFile(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkStdioFile::~morkStdioFile() // assert CloseStdioFile() executed earlier
+{
+  if (mStdioFile_File)
+    CloseStdioFile(mMorkEnv);
+  MORK_ASSERT(mStdioFile_File==0);
+}
+
+/*public non-poly*/ void
+morkStdioFile::CloseStdioFile(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      if ( mStdioFile_File && this->FileActive() && this->FileIoOpen() )
+      {
+        this->CloseStdio(ev);
+      }
+      
+      mStdioFile_File = 0;
+      
+      this->CloseFile(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+// compatible with the morkFile::MakeFile() entry point
+
+/*static*/ morkStdioFile* 
+morkStdioFile::OpenOldStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+  const char* inFilePath, mork_bool inFrozen)
+{
+  morkStdioFile* outFile = 0;
+  if ( ioHeap && inFilePath )
+  {
+    const char* mode = (inFrozen)? "rb" : "rb+";
+    outFile = new(*ioHeap, ev)
+      morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode); 
+      
+    if ( outFile )
+    {
+      outFile->SetFileFrozen(inFrozen);
+    }
+  }
+  else
+    ev->NilPointerError();
+  
+  return outFile;
+}
+
+/*static*/ morkStdioFile* 
+morkStdioFile::CreateNewStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+  const char* inFilePath)
+{
+  morkStdioFile* outFile = 0;
+  if ( ioHeap && inFilePath )
+  {
+    const char* mode = "wb+";
+    outFile = new(*ioHeap, ev)
+      morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode); 
+  }
+  else
+    ev->NilPointerError();
+
+  return outFile;
+}
+
+
+
+NS_IMETHODIMP
+morkStdioFile::BecomeTrunk(nsIMdbEnv* ev)
+  // If this file is a file version branch created by calling AcquireBud(),
+  // BecomeTrunk() causes this file's content to replace the original
+  // file's content, typically by assuming the original file's identity.
+{
+  return Flush(ev);
+}
+
+NS_IMETHODIMP 
+morkStdioFile::AcquireBud(nsIMdbEnv * mdbev, nsIMdbHeap* ioHeap, nsIMdbFile **acquiredFile)
+  // AcquireBud() starts a new "branch" version of the file, empty of content,
+  // so that a new version of the file can be written.  This new file
+  // can later be told to BecomeTrunk() the original file, so the branch
+  // created by budding the file will replace the original file.  Some
+  // file subclasses might initially take the unsafe but expedient
+  // approach of simply truncating this file down to zero length, and
+  // then returning the same morkFile pointer as this, with an extra
+  // reference count increment.  Note that the caller of AcquireBud() is
+  // expected to eventually call CutStrongRef() on the returned file
+  // in order to release the strong reference.  High quality versions
+  // of morkFile subclasses will create entirely new files which later
+  // are renamed to become the old file, so that better transactional
+  // behavior is exhibited by the file, so crashes protect old files.
+  // Note that AcquireBud() is an illegal operation on readonly files.
+{
+  NS_ENSURE_ARG(acquiredFile);
+  MORK_USED_1(ioHeap);
+  nsresult rv = NS_OK;
+  morkFile* outFile = 0;
+  morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+
+  if ( this->IsOpenAndActiveFile() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( file )
+    {
+//#ifdef MORK_WIN
+//      truncate(file, /*eof*/ 0);
+//#else /*MORK_WIN*/
+      char* name = mFile_Name;
+      if ( name )
+      {
+        if ( MORK_FILECLOSE(file) >= 0 )
+        {
+          this->SetFileActive(morkBool_kFalse);
+          this->SetFileIoOpen(morkBool_kFalse);
+          mStdioFile_File = 0;
+          
+          file = MORK_FILEOPEN(name, "wb+"); // open for write, discarding old content
+          if ( file )
+          {
+            mStdioFile_File = file;
+            this->SetFileActive(morkBool_kTrue);
+            this->SetFileIoOpen(morkBool_kTrue);
+            this->SetFileFrozen(morkBool_kFalse);
+          }
+          else
+            this->new_stdio_file_fault(ev);
+        }
+        else
+          this->new_stdio_file_fault(ev);
+      }
+      else
+        this->NilFileNameError(ev);
+      
+//#endif /*MORK_WIN*/
+
+      if ( ev->Good() && this->AddStrongRef(ev->AsMdbEnv()) )
+      {
+        outFile = this;
+        AddRef();
+      }
+    }
+    else if ( mFile_Thief )
+    {
+      rv = mFile_Thief->AcquireBud(ev->AsMdbEnv(), ioHeap, acquiredFile);
+    }
+    else
+      this->NewMissingIoError(ev);
+  }
+  else this->NewFileDownError(ev);
+  
+  *acquiredFile = outFile;
+  return rv;
+}
+
+mork_pos 
+morkStdioFile::Length(morkEnv * ev) const
+{
+  mork_pos outPos = 0;
+  
+  if ( this->IsOpenAndActiveFile() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( file )
+    {
+      long start = MORK_FILETELL(file);
+      if ( start >= 0 )
+      {
+        long fore = MORK_FILESEEK(file, 0, SEEK_END);
+        if ( fore >= 0 )
+        {
+          long eof = MORK_FILETELL(file);
+          if ( eof >= 0 )
+          {
+            long back = MORK_FILESEEK(file, start, SEEK_SET);
+            if ( back >= 0 )
+              outPos = eof;
+            else
+              this->new_stdio_file_fault(ev);
+          }
+          else this->new_stdio_file_fault(ev);
+        }
+        else this->new_stdio_file_fault(ev);
+      }
+      else this->new_stdio_file_fault(ev);
+    }
+    else if ( mFile_Thief )
+      mFile_Thief->Eof(ev->AsMdbEnv(), &outPos);
+    else
+      this->NewMissingIoError(ev);
+  }
+  else this->NewFileDownError(ev);
+
+  return outPos;
+}
+
+NS_IMETHODIMP
+morkStdioFile::Tell(nsIMdbEnv* ev, mork_pos *outPos) const
+{
+  nsresult rv = NS_OK;
+  NS_ENSURE_ARG(outPos);  
+  morkEnv* mev = morkEnv::FromMdbEnv(ev);
+  if ( this->IsOpenAndActiveFile() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( file )
+    {
+      long where = MORK_FILETELL(file);
+      if ( where >= 0 )
+        *outPos = where;
+      else
+        this->new_stdio_file_fault(mev);
+    }
+    else if ( mFile_Thief )
+      mFile_Thief->Tell(ev, outPos);
+    else
+      this->NewMissingIoError(mev);
+  }
+  else this->NewFileDownError(mev);
+
+  return rv;
+}
+
+NS_IMETHODIMP
+morkStdioFile::Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize,  mork_num *outCount)
+{
+  nsresult rv = NS_OK;
+  morkEnv* mev = morkEnv::FromMdbEnv(ev);
+  if ( this->IsOpenAndActiveFile() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( file )
+    {
+      long count = (long) MORK_FILEREAD(outBuf, inSize, file);
+      if ( count >= 0 )
+      {
+        *outCount = (mork_num) count;
+      }
+      else this->new_stdio_file_fault(mev);
+    }
+    else if ( mFile_Thief )
+      mFile_Thief->Read(ev, outBuf, inSize, outCount);
+    else
+      this->NewMissingIoError(mev);
+  }
+  else this->NewFileDownError(mev);
+
+  return rv;
+}
+
+NS_IMETHODIMP 
+morkStdioFile::Seek(nsIMdbEnv* mdbev, mork_pos inPos, mork_pos *aOutPos)
+{
+  mork_pos outPos = 0;
+  nsresult rv = NS_OK;
+  morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+
+  if ( this->IsOpenOrClosingNode() && this->FileActive() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( file )
+    {
+      long where = MORK_FILESEEK(file, inPos, SEEK_SET);
+      if ( where >= 0 )
+        outPos = inPos;
+      else
+        this->new_stdio_file_fault(ev);
+    }
+    else if ( mFile_Thief )
+      mFile_Thief->Seek(mdbev, inPos, aOutPos);
+    else
+      this->NewMissingIoError(ev);
+  }
+  else this->NewFileDownError(ev);
+
+  *aOutPos = outPos;
+  return rv;
+}
+
+NS_IMETHODIMP 
+morkStdioFile::Write(nsIMdbEnv* mdbev, const void* inBuf, mork_size inSize, mork_size *aOutSize)
+{
+  mork_num outCount = 0;
+  nsresult rv = NS_OK;
+  morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+  if ( this->IsOpenActiveAndMutableFile() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( file )
+    {
+      fwrite(inBuf, 1, inSize, file);
+      if ( !ferror(file) )
+        outCount = inSize;
+      else
+        this->new_stdio_file_fault(ev);
+    }
+    else if ( mFile_Thief )
+      mFile_Thief->Write(mdbev, inBuf, inSize, &outCount);
+    else
+      this->NewMissingIoError(ev);
+  }
+  else this->NewFileDownError(ev);
+
+  *aOutSize = outCount;
+  return rv;
+}
+
+NS_IMETHODIMP
+morkStdioFile::Flush(nsIMdbEnv* mdbev)
+{
+  morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+  if ( this->IsOpenOrClosingNode() && this->FileActive() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( file )
+    {
+      MORK_FILEFLUSH(file);
+
+    }
+    else if ( mFile_Thief )
+      mFile_Thief->Flush(mdbev);
+    else
+      this->NewMissingIoError(ev);
+  }
+  else this->NewFileDownError(ev);
+  return NS_OK;
+}
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+//protected: // protected non-poly morkStdioFile methods
+
+void
+morkStdioFile::new_stdio_file_fault(morkEnv* ev) const
+{
+  FILE* file = (FILE*) mStdioFile_File;
+
+  int copyErrno = errno; // facilitate seeing error in debugger
+  
+  //  bunch of stuff not ported here
+  if ( !copyErrno && file )
+  {
+    copyErrno = ferror(file);
+    errno = copyErrno;
+  }
+
+  this->NewFileErrnoError(ev);
+}
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+//public: // public non-poly morkStdioFile methods
+
+
+/*public non-poly*/
+morkStdioFile::morkStdioFile(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkFile(ev, inUsage, ioHeap, ioSlotHeap)
+, mStdioFile_File( 0 )
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kStdioFile;
+}
+
+morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
+  const char* inName, const char* inMode)
+  // calls OpenStdio() after construction
+: morkFile(ev, inUsage, ioHeap, ioSlotHeap)
+, mStdioFile_File( 0 )
+{
+  if ( ev->Good() )
+    this->OpenStdio(ev, inName, inMode);
+}
+
+morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+   nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
+   void* ioFile, const char* inName, mork_bool inFrozen)
+  // calls UseStdio() after construction
+: morkFile(ev, inUsage, ioHeap, ioSlotHeap)
+, mStdioFile_File( 0 )
+{
+  if ( ev->Good() )
+    this->UseStdio(ev, ioFile, inName, inFrozen);
+}
+
+void
+morkStdioFile::OpenStdio(morkEnv* ev, const char* inName, const char* inMode)
+  // Open a new FILE with name inName, using mode flags from inMode.
+{
+  if ( ev->Good() )
+  {
+    if ( !inMode )
+      inMode = "";
+    
+    mork_bool frozen = (*inMode == 'r'); // cursory attempt to note readonly
+    
+    if ( this->IsOpenNode() )
+    {
+      if ( !this->FileActive() )
+      {
+        this->SetFileIoOpen(morkBool_kFalse);
+        if ( inName && *inName )
+        {
+          this->SetFileName(ev, inName);
+          if ( ev->Good() )
+          {
+            FILE* file = MORK_FILEOPEN(inName, inMode);
+            if ( file )
+            {
+              mStdioFile_File = file;
+              this->SetFileActive(morkBool_kTrue);
+              this->SetFileIoOpen(morkBool_kTrue);
+              this->SetFileFrozen(frozen);
+            }
+            else
+              this->new_stdio_file_fault(ev);
+          }
+        }
+        else ev->NewError("no file name");
+      }
+      else ev->NewError("file already active");
+    }
+    else this->NewFileDownError(ev);
+  }
+}
+
+void
+morkStdioFile::UseStdio(morkEnv* ev, void* ioFile, const char* inName,
+  mork_bool inFrozen)
+  // Use an existing file, like stdin/stdout/stderr, which should not
+  // have the io stream closed when the file is closed.  The ioFile
+  // parameter must actually be of type FILE (but we don't want to make
+  // this header file include the stdio.h header file).
+{
+  if ( ev->Good() )
+  {
+    if ( this->IsOpenNode() )
+    {
+      if ( !this->FileActive() )
+      {
+        if ( ioFile )
+        {
+          this->SetFileIoOpen(morkBool_kFalse);
+          this->SetFileName(ev, inName);
+          if ( ev->Good() )
+          {
+            mStdioFile_File = ioFile;
+            this->SetFileActive(morkBool_kTrue);
+            this->SetFileFrozen(inFrozen);
+          }
+        }
+        else
+          ev->NilPointerError();
+      }
+      else ev->NewError("file already active");
+    }
+    else this->NewFileDownError(ev);
+  }
+}
+
+void
+morkStdioFile::CloseStdio(morkEnv* ev)
+  // Close the stream io if both and FileActive() and FileIoOpen(), but
+  // this does not close this instances (like CloseStdioFile() does).
+  // If stream io was made active by means of calling UseStdio(),
+  // then this method does little beyond marking the stream inactive
+  // because FileIoOpen() is false.
+{
+  if ( mStdioFile_File && this->FileActive() && this->FileIoOpen() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( MORK_FILECLOSE(file) < 0 )
+      this->new_stdio_file_fault(ev);
+    
+    mStdioFile_File = 0;
+    this->SetFileActive(morkBool_kFalse);
+    this->SetFileIoOpen(morkBool_kFalse);
+  }
+}
+
+
+NS_IMETHODIMP
+morkStdioFile::Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief)
+  // If this file is a file version branch created by calling AcquireBud(),
+  // BecomeTrunk() causes this file's content to replace the original
+  // file's content, typically by assuming the original file's identity.
+{
+  morkEnv *mev = morkEnv::FromMdbEnv(ev);
+  if ( mStdioFile_File && FileActive() && FileIoOpen() )
+  {
+    FILE* file = (FILE*) mStdioFile_File;
+    if ( MORK_FILECLOSE(file) < 0 )
+      new_stdio_file_fault(mev);
+    
+    mStdioFile_File = 0;
+  }
+  SetThief(mev, ioThief);
+  return NS_OK;
+}
+
+
+#if defined(MORK_WIN)
+
+void mork_fileflush(FILE * file)
+{
+  fflush(file);
+#ifndef WINCE
+  OSVERSIONINFOA vi = { sizeof(OSVERSIONINFOA) };
+  if ((GetVersionExA(&vi) && vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS))
+  {
+    // Win9x/ME
+    int fd = fileno(file);
+    HANDLE fh = (HANDLE)_get_osfhandle(fd);
+    FlushFileBuffers(fh);
+  }
+#endif
+}
+
+#endif /*MORK_WIN*/
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkFile.h
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKFILE_
+#define _MORKFILE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*=============================================================================
+ * morkFile: abstract file interface
+ */
+
+#define morkDerived_kFile     /*i*/ 0x4669 /* ascii 'Fi' */
+
+class morkFile /*d*/ : public morkObject, public nsIMdbFile { /* ````` simple file API ````` */
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*    mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+  
+// public: // slots inherited from morkObject (meant to inform only)
+
+  // mork_color   mBead_Color;   // ID for this bead
+  // morkHandle*  mObject_Handle;  // weak ref to handle for this object
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+protected: // protected morkFile members (similar to public domain IronDoc)
+
+  mork_u1     mFile_Frozen;   // 'F' => file allows only read access
+  mork_u1     mFile_DoTrace;  // 'T' trace if ev->DoTrace()
+  mork_u1     mFile_IoOpen;   // 'O' => io stream is open (& needs a close)
+  mork_u1     mFile_Active;   // 'A' => file is active and usable
+  
+  nsIMdbHeap* mFile_SlotHeap; // heap for Name and other allocated slots
+  char*       mFile_Name; // can be nil if SetFileName() is never called
+  // mFile_Name convention: managed with morkEnv::CopyString()/FreeString()
+
+  nsIMdbFile* mFile_Thief; // from a call to orkinFile::Steal()
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  NS_DECL_ISUPPORTS_INHERITED
+  virtual void CloseMorkNode(morkEnv* ev); // CloseFile() only if open
+  virtual ~morkFile(); // assert that CloseFile() executed earlier
+  
+public: // morkFile construction & destruction
+  morkFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+    nsIMdbHeap* ioSlotHeap);
+  void CloseFile(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkFile(const morkFile& other);
+  morkFile& operator=(const morkFile& other);
+
+public: // dynamic type identification
+  mork_bool IsFile() const
+  { return IsNode() && mNode_Derived == morkDerived_kFile; }
+// } ===== end morkNode methods =====
+    
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public static standard file creation entry point
+
+  static morkFile* OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+    const char* inFilePath, mork_bool inFrozen);
+  // Choose some subclass of morkFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be open and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+
+  static morkFile* CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+    const char* inFilePath);
+  // Choose some subclass of morkFile to instantiate, in order to read
+  // (and write if not frozen) the file known by inFilePath.  The file
+  // returned should be created and ready for use, and presumably positioned
+  // at the first byte position of the file.  The exact manner in which
+  // files must be opened is considered a subclass specific detail, and
+  // other portions or Mork source code don't want to know how it's done.
+  
+public: // non-poly morkFile methods
+
+  mork_bool FileFrozen() const  { return mFile_Frozen == 'F'; }
+  mork_bool FileDoTrace() const { return mFile_DoTrace == 'T'; }
+  mork_bool FileIoOpen() const  { return mFile_IoOpen == 'O'; }
+  mork_bool FileActive() const  { return mFile_Active == 'A'; }
+
+  void SetFileFrozen(mork_bool b)  { mFile_Frozen = (mork_u1) ((b)? 'F' : 0); }
+  void SetFileDoTrace(mork_bool b) { mFile_DoTrace = (mork_u1) ((b)? 'T' : 0); }
+  void SetFileIoOpen(mork_bool b)  { mFile_IoOpen = (mork_u1) ((b)? 'O' : 0); }
+  void SetFileActive(mork_bool b)  { mFile_Active = (mork_u1) ((b)? 'A' : 0); }
+
+  
+  mork_bool IsOpenActiveAndMutableFile() const
+  { return ( IsOpenNode() && FileActive() && !FileFrozen() ); }
+    // call IsOpenActiveAndMutableFile() before writing a file
+  
+  mork_bool IsOpenAndActiveFile() const
+  { return ( this->IsOpenNode() && this->FileActive() ); }
+    // call IsOpenAndActiveFile() before using a file
+    
+
+  nsIMdbFile* GetThief() const { return mFile_Thief; }
+  void SetThief(morkEnv* ev, nsIMdbFile* ioThief); // ioThief can be nil
+    
+  const char* GetFileNameString() const { return mFile_Name; }
+  void SetFileName(morkEnv* ev, const char* inName); // inName can be nil
+  static void NilSlotHeapError(morkEnv* ev);
+  static void NilFileNameError(morkEnv* ev);
+  static void NonFileTypeError(morkEnv* ev);
+    
+  void NewMissingIoError(morkEnv* ev) const;
+    
+  void NewFileDownError(morkEnv* ev) const;
+    // call NewFileDownError() when either IsOpenAndActiveFile()
+    // is false, or when IsOpenActiveAndMutableFile() is false.
+ 
+   void NewFileErrnoError(morkEnv* ev) const;
+       // call NewFileErrnoError() to convert std C errno into AB fault
+
+  mork_size WriteNewlines(morkEnv* ev, mork_count inNewlines);
+  // WriteNewlines() returns the number of bytes written.
+         
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakFile(morkFile* me,
+    morkEnv* ev, morkFile** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongFile(morkFile* me,
+    morkEnv* ev, morkFile** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+public:
+  virtual mork_pos   Length(morkEnv* ev) const = 0; // eof
+  // nsIMdbFile methods
+  NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const = 0;
+  NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos) = 0;
+  NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos);
+  // } ----- end pos methods -----
+
+  // { ----- begin read methods -----
+  NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+    mdb_size* outActualSize) = 0;
+  NS_IMETHOD Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+    mdb_pos inPos, mdb_size* outActualSize);
+  // } ----- end read methods -----
+    
+  // { ----- begin write methods -----
+  NS_IMETHOD  Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+    mdb_size* outActualSize) = 0;
+  NS_IMETHOD  Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+    mdb_pos inPos, mdb_size* outActualSize);
+  NS_IMETHOD  Flush(nsIMdbEnv* ev) = 0;
+  // } ----- end attribute methods -----
+    
+  // { ----- begin path methods -----
+  NS_IMETHOD  Path(nsIMdbEnv* ev, mdbYarn* outFilePath);
+  // } ----- end path methods -----
+    
+  // { ----- begin replacement methods -----
+  NS_IMETHOD  Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) = 0;
+  NS_IMETHOD  Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief);
+  // } ----- end replacement methods -----
+
+  // { ----- begin versioning methods -----
+  NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) = 0;
+
+  NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+    nsIMdbFile** acqBud) = 0; 
+  // } ----- end versioning methods -----
+
+// } ===== end nsIMdbFile methods =====
+
+};
+
+/*=============================================================================
+ * morkStdioFile: concrete file using standard C file io
+ */
+
+#define morkDerived_kStdioFile     /*i*/ 0x7346 /* ascii 'sF' */
+
+class morkStdioFile /*d*/ : public morkFile { /* `` copied from IronDoc `` */
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+protected: // protected morkStdioFile members
+
+  void* mStdioFile_File;
+  // actually type FILE*, but using opaque void* type
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseStdioFile() only if open
+  virtual ~morkStdioFile(); // assert that CloseStdioFile() executed earlier
+  
+public: // morkStdioFile construction & destruction
+  morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseStdioFile(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkStdioFile(const morkStdioFile& other);
+  morkStdioFile& operator=(const morkStdioFile& other);
+
+public: // dynamic type identification
+  mork_bool IsStdioFile() const
+  { return IsNode() && mNode_Derived == morkDerived_kStdioFile; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonStdioFileTypeError(morkEnv* ev);
+    
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // compatible with the morkFile::OpenOldFile() entry point
+
+  static morkStdioFile* OpenOldStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+    const char* inFilePath, mork_bool inFrozen);
+
+  static morkStdioFile* CreateNewStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+    const char* inFilePath);
+
+  virtual mork_pos   Length(morkEnv* ev) const; // eof
+
+  NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const;
+  NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos);
+//  NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos);
+  // } ----- end pos methods -----
+
+  // { ----- begin read methods -----
+  NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+    mdb_size* outActualSize);
+    
+  // { ----- begin write methods -----
+  NS_IMETHOD  Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+    mdb_size* outActualSize);
+//  NS_IMETHOD  Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+//    mdb_pos inPos, mdb_size* outActualSize);
+  NS_IMETHOD  Flush(nsIMdbEnv* ev);
+  // } ----- end attribute methods -----
+    
+  NS_IMETHOD  Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief);
+   
+
+  // { ----- begin versioning methods -----
+  NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev);
+
+  NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+    nsIMdbFile** acqBud); 
+  // } ----- end versioning methods -----
+
+// } ===== end nsIMdbFile methods =====
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+protected: // protected non-poly morkStdioFile methods
+
+  void new_stdio_file_fault(morkEnv* ev) const;
+    
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public non-poly morkStdioFile methods
+    
+  morkStdioFile(morkEnv* ev, const morkUsage& inUsage, 
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
+    const char* inName, const char* inMode);
+    // calls OpenStdio() after construction
+
+  morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
+     void* ioFile, const char* inName, mork_bool inFrozen);
+    // calls UseStdio() after construction
+  
+  void OpenStdio(morkEnv* ev, const char* inName, const char* inMode);
+    // Open a new FILE with name inName, using mode flags from inMode.
+  
+  void UseStdio(morkEnv* ev, void* ioFile, const char* inName,
+    mork_bool inFrozen);
+    // Use an existing file, like stdin/stdout/stderr, which should not
+    // have the io stream closed when the file is closed.  The ioFile
+    // parameter must actually be of type FILE (but we don't want to make
+    // this header file include the stdio.h header file).
+    
+  void CloseStdio(morkEnv* ev);
+    // Close the stream io if both and FileActive() and FileIoOpen(), but
+    // this does not close this instances (like CloseStdioFile() does).
+    // If stream io was made active by means of calling UseStdio(),
+    // then this method does little beyond marking the stream inactive
+    // because FileIoOpen() is false.
+    
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakStdioFile(morkStdioFile* me,
+    morkEnv* ev, morkStdioFile** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongStdioFile(morkStdioFile* me,
+    morkEnv* ev, morkStdioFile** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKFILE_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkHandle.cpp
@@ -0,0 +1,460 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKFACTORY_
+#include "morkFactory.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKHANDLE_
+#include "morkHandle.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkHandle::CloseMorkNode(morkEnv* ev) // CloseHandle() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseHandle(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkHandle::~morkHandle() // assert CloseHandle() executed earlier
+{
+  MORK_ASSERT(mHandle_Env==0);
+  MORK_ASSERT(mHandle_Face==0);
+  MORK_ASSERT(mHandle_Object==0);
+  MORK_ASSERT(mHandle_Magic==0);
+  MORK_ASSERT(mHandle_Tag==morkHandle_kTag); // should still have correct tag
+}
+
+/*public non-poly*/
+morkHandle::morkHandle(morkEnv* ev, // note morkUsage is always morkUsage_kPool
+    morkHandleFace* ioFace,  // must not be nil, cookie for this handle
+    morkObject* ioObject,    // must not be nil, the object for this handle
+    mork_magic inMagic)      // magic sig to denote specific subclass
+: morkNode(ev, morkUsage::kPool, (nsIMdbHeap*) 0L)
+, mHandle_Tag( 0 )
+, mHandle_Env( ev )
+, mHandle_Face( ioFace )
+, mHandle_Object( 0 )
+, mHandle_Magic( 0 )
+{
+  if ( ioFace && ioObject )
+  {
+    if ( ev->Good() )
+    {
+      mHandle_Tag = morkHandle_kTag;
+      morkObject::SlotStrongObject(ioObject, ev, &mHandle_Object);
+      morkHandle::SlotWeakHandle(this, ev, &ioObject->mObject_Handle);
+      if ( ev->Good() )
+      {
+        mHandle_Magic = inMagic;
+        mNode_Derived = morkDerived_kHandle;
+      }
+    }
+    else
+      ev->CantMakeWhenBadError();
+  }
+  else
+    ev->NilPointerError();
+}
+
+/*public non-poly*/ void
+morkHandle::CloseHandle(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      morkObject* obj = mHandle_Object;
+      mork_bool objDidRefSelf = ( obj && obj->mObject_Handle == this );
+      if ( objDidRefSelf )
+        obj->mObject_Handle = 0; // drop the reference
+      
+      morkObject::SlotStrongObject((morkObject*) 0, ev, &mHandle_Object);
+      mHandle_Magic = 0;
+      // note mHandle_Tag MUST stay morkHandle_kTag for morkNode::ZapOld()
+      this->MarkShut();
+
+      if ( objDidRefSelf )
+        this->CutWeakRef(ev); // do last, because it might self destroy
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+void morkHandle::NilFactoryError(morkEnv* ev) const
+{
+  ev->NewError("nil mHandle_Factory");
+}
+  
+void morkHandle::NilHandleObjectError(morkEnv* ev) const
+{
+  ev->NewError("nil mHandle_Object");
+}
+  
+void morkHandle::NonNodeObjectError(morkEnv* ev) const
+{
+  ev->NewError("non-node mHandle_Object");
+}
+  
+void morkHandle::NonOpenObjectError(morkEnv* ev) const
+{
+  ev->NewError("non-open mHandle_Object");
+}
+  
+void morkHandle::NewBadMagicHandleError(morkEnv* ev, mork_magic inMagic) const
+{
+  MORK_USED_1(inMagic);
+  ev->NewError("wrong mHandle_Magic");
+}
+
+void morkHandle::NewDownHandleError(morkEnv* ev) const
+{
+  if ( this->IsHandle() )
+  {
+    if ( this->GoodHandleTag() )
+    {
+      if ( this->IsOpenNode() )
+        ev->NewError("unknown down morkHandle error");
+      else
+        this->NonOpenNodeError(ev);
+    }
+    else
+      ev->NewError("wrong morkHandle tag");
+  }
+  else
+    ev->NewError("non morkHandle");
+}
+
+morkObject* morkHandle::GetGoodHandleObject(morkEnv* ev,
+  mork_bool inMutable, mork_magic inMagicType, mork_bool inClosedOkay) const
+{
+  morkObject* outObject = 0;
+  if ( this->IsHandle() && this->GoodHandleTag() &&
+    ( inClosedOkay || this->IsOpenNode() ) )
+  {
+    if ( !inMagicType || mHandle_Magic == inMagicType )
+    {
+      morkObject* obj = this->mHandle_Object;
+      if ( obj )
+      {
+        if ( obj->IsNode() )
+        {
+          if ( inClosedOkay || obj->IsOpenNode() )
+          {
+            if ( this->IsMutable() || !inMutable )
+              outObject = obj;
+            else
+              this->NonMutableNodeError(ev);
+          }
+          else
+            this->NonOpenObjectError(ev);
+        }
+        else
+          this->NonNodeObjectError(ev);
+      }
+      else if ( !inClosedOkay )
+        this->NilHandleObjectError(ev);
+    }
+    else
+      this->NewBadMagicHandleError(ev, inMagicType);
+  }
+  else
+    this->NewDownHandleError(ev);
+  
+  MORK_ASSERT(outObject || inClosedOkay);
+  return outObject;
+}
+
+
+morkEnv*
+morkHandle::CanUseHandle(nsIMdbEnv* mev, mork_bool inMutable,
+  mork_bool inClosedOkay, mdb_err* outErr) const
+{
+  morkEnv* outEnv = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkObject* obj = this->GetGoodHandleObject(ev, inMutable,
+      /*magic*/ 0, inClosedOkay);
+    if ( obj )
+    {
+      outEnv = ev;
+    }
+    *outErr = ev->AsErr();
+  }
+  MORK_ASSERT(outEnv || inClosedOkay);
+  return outEnv;
+}
+
+// { ===== begin nsIMdbObject methods =====
+
+// { ----- begin attribute methods -----
+/*virtual*/ mdb_err
+morkHandle::Handle_IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly)
+{
+  mdb_err outErr = 0;
+  mdb_bool readOnly = mdbBool_kTrue;
+  
+  morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kTrue, &outErr);
+  if ( ev )
+  {
+    readOnly = mHandle_Object->IsFrozen();
+    
+    outErr = ev->AsErr();
+  }
+  MORK_ASSERT(outIsReadonly);
+  if ( outIsReadonly )
+    *outIsReadonly = readOnly;
+
+  return outErr;
+}
+// same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+// } ----- end attribute methods -----
+
+// { ----- begin factory methods -----
+/*virtual*/ mdb_err
+morkHandle::Handle_GetMdbFactory(nsIMdbEnv* mev, nsIMdbFactory** acqFactory)
+{
+  mdb_err outErr = 0;
+  nsIMdbFactory* handle = 0;
+  
+  morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kTrue, &outErr);
+  if ( ev )
+  {
+    morkFactory* factory = ev->mEnv_Factory;
+    if ( factory )
+    {
+      handle = factory;
+      NS_ADDREF(handle);
+    }
+    else
+      this->NilFactoryError(ev);
+      
+    outErr = ev->AsErr();
+  }
+
+  MORK_ASSERT(acqFactory);
+  if ( acqFactory )
+    *acqFactory = handle;
+
+  return outErr;
+} 
+// } ----- end factory methods -----
+
+// { ----- begin ref counting for well-behaved cyclic graphs -----
+/*virtual*/ mdb_err
+morkHandle::Handle_GetWeakRefCount(nsIMdbEnv* mev, // weak refs
+  mdb_count* outCount)
+{
+  mdb_err outErr = 0;
+  mdb_count count = 0;
+  
+  morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kTrue, &outErr);
+  if ( ev )
+  {
+    count = this->WeakRefsOnly();
+    
+    outErr = ev->AsErr();
+  }
+  MORK_ASSERT(outCount);
+  if ( outCount )
+    *outCount = count;
+    
+  return outErr;
+}  
+/*virtual*/ mdb_err
+morkHandle::Handle_GetStrongRefCount(nsIMdbEnv* mev, // strong refs
+  mdb_count* outCount)
+{
+  mdb_err outErr = 0;
+  mdb_count count = 0;
+  
+  morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kTrue, &outErr);
+  if ( ev )
+  {
+    count = this->StrongRefsOnly();
+    
+    outErr = ev->AsErr();
+  }
+  MORK_ASSERT(outCount);
+  if ( outCount )
+    *outCount = count;
+    
+  return outErr;
+}
+
+/*virtual*/ mdb_err
+morkHandle::Handle_AddWeakRef(nsIMdbEnv* mev)
+{
+  mdb_err outErr = 0;
+  
+  morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kTrue, &outErr);
+  if ( ev )
+  {
+    this->AddWeakRef(ev);
+    outErr = ev->AsErr();
+  }
+    
+  return outErr;
+}
+/*virtual*/ mdb_err
+morkHandle::Handle_AddStrongRef(nsIMdbEnv* mev)
+{
+  mdb_err outErr = 0;
+  
+  morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    this->AddStrongRef(ev);
+    outErr = ev->AsErr();
+  }
+    
+  return outErr;
+}
+
+/*virtual*/ mdb_err
+morkHandle::Handle_CutWeakRef(nsIMdbEnv* mev)
+{
+  mdb_err outErr = 0;
+  
+  morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kTrue, &outErr);
+  if ( ev )
+  {
+    this->CutWeakRef(ev);
+    outErr = ev->AsErr();
+  }
+    
+  return outErr;
+}
+/*virtual*/ mdb_err
+morkHandle::Handle_CutStrongRef(nsIMdbEnv* mev)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kTrue, &outErr);
+  if ( ev )
+  {
+    this->CutStrongRef(ev);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+/*virtual*/ mdb_err
+morkHandle::Handle_CloseMdbObject(nsIMdbEnv* mev)
+// called at strong refs zero
+{
+  // if only one ref, Handle_CutStrongRef will clean up better.
+  if (mNode_Uses == 1)
+    return Handle_CutStrongRef(mev);
+
+  mdb_err outErr = 0;
+  
+  if ( this->IsNode() && this->IsOpenNode() )
+  {
+    morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+    /*inClosedOkay*/ morkBool_kTrue, &outErr);
+    if ( ev )
+    {
+      morkObject* object = mHandle_Object;
+      if ( object && object->IsNode() && object->IsOpenNode() )
+        object->CloseMorkNode(ev);
+        
+      this->CloseMorkNode(ev);
+      outErr = ev->AsErr();
+    }
+  }
+  return outErr;
+}
+
+/*virtual*/ mdb_err
+morkHandle::Handle_IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
+{
+  MORK_USED_1(mev);
+  mdb_err outErr = 0;
+  
+  MORK_ASSERT(outOpen);
+  if ( outOpen )
+    *outOpen = this->IsOpenNode();
+      
+  return outErr;
+}
+// } ----- end ref counting -----
+
+// } ===== end nsIMdbObject methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkHandle.h
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKHANDLE_
+#define _MORKHANDLE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class morkPool;
+class morkObject;
+class morkFactory;
+
+#define morkDerived_kHandle     /*i*/ 0x486E /* ascii 'Hn' */
+#define morkHandle_kTag   0x68416E44 /* ascii 'hAnD' */
+
+/*| morkHandle: 
+|*/
+class morkHandle : public morkNode {
+  
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*    mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+  mork_u4         mHandle_Tag;     // must equal morkHandle_kTag
+  morkEnv*        mHandle_Env;     // pool that allocated this handle
+  morkHandleFace* mHandle_Face;    // cookie from pool containing this
+  morkObject*     mHandle_Object;  // object this handle wraps for MDB API
+  mork_magic      mHandle_Magic;   // magic sig different in each subclass
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseHandle() only if open
+  virtual ~morkHandle(); // assert that CloseHandle() executed earlier
+  
+public: // morkHandle construction & destruction
+  morkHandle(morkEnv* ev, // note morkUsage is always morkUsage_kPool
+    morkHandleFace* ioFace,  // must not be nil, cookie for this handle
+    morkObject* ioObject,    // must not be nil, the object for this handle
+    mork_magic inMagic);     // magic sig to denote specific subclass
+  void CloseHandle(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkHandle(const morkHandle& other);
+  morkHandle& operator=(const morkHandle& other);
+  
+protected: // special case empty construction for morkHandleFrame
+  friend class morkHandleFrame;
+  morkHandle() { }
+
+public: // dynamic type identification
+  mork_bool IsHandle() const
+  { return IsNode() && mNode_Derived == morkDerived_kHandle; }
+// } ===== end morkNode methods =====
+
+public: // morkHandle memory management operators
+  void* operator new(size_t inSize, morkPool& ioPool, morkZone& ioZone, morkEnv* ev) CPP_THROW_NEW
+  { return ioPool.NewHandle(ev, inSize, &ioZone); }
+  
+  void* operator new(size_t inSize, morkPool& ioPool, morkEnv* ev) CPP_THROW_NEW
+  { return ioPool.NewHandle(ev, inSize, (morkZone*) 0); }
+  
+  void* operator new(size_t inSize, morkHandleFace* ioFace) CPP_THROW_NEW
+  { MORK_USED_1(inSize); return ioFace; }
+  
+
+public: // other handle methods
+  mork_bool GoodHandleTag() const
+  { return mHandle_Tag == morkHandle_kTag; }
+  
+  void NewBadMagicHandleError(morkEnv* ev, mork_magic inMagic) const;
+  void NewDownHandleError(morkEnv* ev) const;
+  void NilFactoryError(morkEnv* ev) const;
+  void NilHandleObjectError(morkEnv* ev) const;
+  void NonNodeObjectError(morkEnv* ev) const;
+  void NonOpenObjectError(morkEnv* ev) const;
+  
+  morkObject* GetGoodHandleObject(morkEnv* ev,
+    mork_bool inMutable, mork_magic inMagicType, mork_bool inClosedOkay) const;
+
+public: // interface supporting mdbObject methods
+
+  morkEnv* CanUseHandle(nsIMdbEnv* mev, mork_bool inMutable,
+    mork_bool inClosedOkay, mdb_err* outErr) const;
+    
+  // { ----- begin mdbObject style methods -----
+  mdb_err Handle_IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly);
+
+  mdb_err Handle_GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory); 
+  mdb_err Handle_GetWeakRefCount(nsIMdbEnv* ev,  mdb_count* outCount);  
+  mdb_err Handle_GetStrongRefCount(nsIMdbEnv* ev, mdb_count* outCount);
+
+  mdb_err Handle_AddWeakRef(nsIMdbEnv* ev);
+  mdb_err Handle_AddStrongRef(nsIMdbEnv* ev);
+
+  mdb_err Handle_CutWeakRef(nsIMdbEnv* ev);
+  mdb_err Handle_CutStrongRef(nsIMdbEnv* ev);
+  
+  mdb_err Handle_CloseMdbObject(nsIMdbEnv* ev);
+  mdb_err Handle_IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen);
+  // } ----- end mdbObject style methods -----
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakHandle(morkHandle* me,
+    morkEnv* ev, morkHandle** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongHandle(morkHandle* me,
+    morkEnv* ev, morkHandle** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+#define morkHandleFrame_kPadSlotCount 4
+
+/*| morkHandleFrame: an object format used for allocating and maintaining
+**| instances of morkHandle, with leading slots used to maintain this in a
+**| linked list, and following slots to provide extra footprint that might
+**| be needed by any morkHandle subclasses that include very little extra
+**| space (by virtue of the fact that each morkHandle subclass is expected
+**| to multiply inherit from another base class that has only abstact methods
+**| for space overhead related only to some vtable representation).
+|*/
+class morkHandleFrame {
+public:
+  morkLink    mHandleFrame_Link;    // list storage without trampling Handle
+  morkHandle  mHandleFrame_Handle;
+  mork_ip     mHandleFrame_Padding[ morkHandleFrame_kPadSlotCount ];
+  
+public:
+  morkHandle*  AsHandle() { return &mHandleFrame_Handle; }
+  
+  morkHandleFrame() {}  // actually, morkHandleFrame never gets constructed
+
+private: // copying is not allowed
+  morkHandleFrame(const morkHandleFrame& other);
+  morkHandleFrame& operator=(const morkHandleFrame& other);
+};
+
+#define morkHandleFrame_kHandleOffset \
+  mork_OffsetOf(morkHandleFrame,mHandleFrame_Handle)
+  
+#define morkHandle_AsHandleFrame(h) ((h)->mHandle_Block , \
+ ((morkHandleFrame*) (((mork_u1*)(h)) - morkHandleFrame_kHandleOffset)))
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKHANDLE_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkIntMap.cpp
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkIntMap::CloseMorkNode(morkEnv* ev) // CloseIntMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseIntMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkIntMap::~morkIntMap() // assert CloseIntMap() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkIntMap::morkIntMap(morkEnv* ev,
+  const morkUsage& inUsage, mork_size inValSize,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges)
+: morkMap(ev, inUsage, ioHeap, sizeof(mork_u4), inValSize,
+  morkIntMap_kStartSlotCount, ioSlotHeap, inHoldChanges)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kIntMap;
+}
+
+/*public non-poly*/ void
+morkIntMap::CloseIntMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      this->CloseMap(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+// { ===== begin morkMap poly interface =====
+/*virtual*/ mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB)
+morkIntMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const
+{
+  MORK_USED_1(ev);
+  return *((const mork_u4*) inKeyA) == *((const mork_u4*) inKeyB);
+}
+
+/*virtual*/ mork_u4 // some integer function of *((mork_u4*) inKey)
+morkIntMap::Hash(morkEnv* ev, const void* inKey) const
+{
+  MORK_USED_1(ev);
+  return *((const mork_u4*) inKey);
+}
+// } ===== end morkMap poly interface =====
+
+mork_bool
+morkIntMap::AddInt(morkEnv* ev, mork_u4 inKey, void* ioAddress)
+  // the AddInt() method return value equals ev->Good().
+{
+  if ( ev->Good() )
+  {
+    this->Put(ev, &inKey, &ioAddress, 
+      /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+  }
+    
+  return ev->Good();
+}
+
+mork_bool
+morkIntMap::CutInt(morkEnv* ev, mork_u4 inKey)
+{
+  return this->Cut(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0,
+    (mork_change**) 0);
+}
+
+void*
+morkIntMap::GetInt(morkEnv* ev, mork_u4 inKey)
+  // Note the returned val does NOT have an increase in refcount for this.
+{
+  void* val = 0; // old val in the map
+  this->Get(ev, &inKey, /*key*/ (void*) 0, &val, (mork_change**) 0);
+  
+  return val;
+}
+
+mork_bool
+morkIntMap::HasInt(morkEnv* ev, mork_u4 inKey)
+  // Note the returned val does NOT have an increase in refcount for this.
+{
+  return this->Get(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0, 
+    (mork_change**) 0);
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#ifdef MORK_POINTER_MAP_IMPL
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkPointerMap::CloseMorkNode(morkEnv* ev) // ClosePointerMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->ClosePointerMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkPointerMap::~morkPointerMap() // assert ClosePointerMap() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkPointerMap::morkPointerMap(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkMap(ev, inUsage, ioHeap, sizeof(void*), sizeof(void*),
+  morkPointerMap_kStartSlotCount, ioSlotHeap,
+  /*inHoldChanges*/ morkBool_kFalse)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kPointerMap;
+}
+
+/*public non-poly*/ void
+morkPointerMap::ClosePointerMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      this->CloseMap(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+// { ===== begin morkMap poly interface =====
+/*virtual*/ mork_bool // *((void**) inKeyA) == *((void**) inKeyB)
+morkPointerMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const
+{
+  MORK_USED_1(ev);
+  return *((const void**) inKeyA) == *((const void**) inKeyB);
+}
+
+/*virtual*/ mork_u4 // some integer function of *((mork_u4*) inKey)
+morkPointerMap::Hash(morkEnv* ev, const void* inKey) const
+{
+  MORK_USED_1(ev);
+  return *((const mork_u4*) inKey);
+}
+// } ===== end morkMap poly interface =====
+
+mork_bool
+morkPointerMap::AddPointer(morkEnv* ev, void* inKey, void* ioAddress)
+  // the AddPointer() method return value equals ev->Good().
+{
+  if ( ev->Good() )
+  {
+    this->Put(ev, &inKey, &ioAddress, 
+      /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+  }
+    
+  return ev->Good();
+}
+
+mork_bool
+morkPointerMap::CutPointer(morkEnv* ev, void* inKey)
+{
+  return this->Cut(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0,
+    (mork_change**) 0);
+}
+
+void*
+morkPointerMap::GetPointer(morkEnv* ev, void* inKey)
+  // Note the returned val does NOT have an increase in refcount for this.
+{
+  void* val = 0; // old val in the map
+  this->Get(ev, &inKey, /*key*/ (void*) 0, &val, (mork_change**) 0);
+  
+  return val;
+}
+
+mork_bool
+morkPointerMap::HasPointer(morkEnv* ev, void* inKey)
+  // Note the returned val does NOT have an increase in refcount for this.
+{
+  return this->Get(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0, 
+    (mork_change**) 0);
+}
+#endif /*MORK_POINTER_MAP_IMPL*/
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkIntMap.h
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKINTMAP_
+#define _MORKINTMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kIntMap  /*i*/ 0x694D /* ascii 'iM' */
+
+#define morkIntMap_kStartSlotCount 256
+
+/*| morkIntMap: maps mork_token -> morkNode
+|*/
+class morkIntMap : public morkMap { // for mapping tokens to maps
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseIntMap() only if open
+  virtual ~morkIntMap(); // assert that CloseIntMap() executed earlier
+  
+public: // morkMap construction & destruction
+
+  // keySize for morkIntMap equals sizeof(mork_u4)
+  morkIntMap(morkEnv* ev, const morkUsage& inUsage, mork_size inValSize,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges);
+  void CloseIntMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsIntMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kIntMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+  virtual mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB)
+  Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const;
+
+  virtual mork_u4 // some integer function of *((mork_u4*) inKey)
+  Hash(morkEnv* ev, const void* inKey) const;
+// } ===== end morkMap poly interface =====
+
+public: // other map methods
+
+  mork_bool  AddInt(morkEnv* ev, mork_u4 inKey, void* ioAddress);
+  // the AddInt() boolean return equals ev->Good().
+
+  mork_bool  CutInt(morkEnv* ev, mork_u4 inKey);
+  // The CutInt() boolean return indicates whether removal happened. 
+  
+  void*      GetInt(morkEnv* ev, mork_u4 inKey);
+  // Note the returned node does NOT have an increase in refcount for this.
+  
+  mork_bool  HasInt(morkEnv* ev, mork_u4 inKey);
+  // Note the returned node does NOT have an increase in refcount for this.
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#ifdef MORK_POINTER_MAP_IMPL
+
+#define morkDerived_kPointerMap  /*i*/ 0x704D /* ascii 'pM' */
+
+#define morkPointerMap_kStartSlotCount 256
+
+/*| morkPointerMap: maps void* -> void*
+**|
+**| This pointer map class is equivalent to morkIntMap when sizeof(mork_u4)
+**| equals sizeof(void*).  However, when these two sizes are different,
+**| then we cannot use the same hash table structure very easily without
+**| being very careful about the size and usage assumptions of those
+**| clients using the smaller data type.  So we just go ahead and use
+**| morkPointerMap for hash tables using pointer key types.
+|*/
+class morkPointerMap : public morkMap { // for mapping tokens to maps
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // ClosePointerMap() only if open
+  virtual ~morkPointerMap(); // assert that ClosePointerMap() executed earlier
+  
+public: // morkMap construction & destruction
+
+  // keySize for morkPointerMap equals sizeof(mork_u4)
+  morkPointerMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+  void ClosePointerMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsPointerMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kPointerMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+  virtual mork_bool // *((void**) inKeyA) == *((void**) inKeyB)
+  Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const;
+
+  virtual mork_u4 // some integer function of *((mork_u4*) inKey)
+  Hash(morkEnv* ev, const void* inKey) const;
+// } ===== end morkMap poly interface =====
+
+public: // other map methods
+
+  mork_bool  AddPointer(morkEnv* ev, void* inKey, void* ioAddress);
+  // the AddPointer() boolean return equals ev->Good().
+
+  mork_bool  CutPointer(morkEnv* ev, void* inKey);
+  // The CutPointer() boolean return indicates whether removal happened. 
+  
+  void*      GetPointer(morkEnv* ev, void* inKey);
+  // Note the returned node does NOT have an increase in refcount for this.
+  
+  mork_bool  HasPointer(morkEnv* ev, void* inKey);
+  // Note the returned node does NOT have an increase in refcount for this.
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakIntMap(morkIntMap* me,
+    morkEnv* ev, morkIntMap** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongIntMap(morkIntMap* me,
+    morkEnv* ev, morkIntMap** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+};
+#endif /*MORK_POINTER_MAP_IMPL*/
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKINTMAP_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkMap.cpp
@@ -0,0 +1,960 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This code is mostly a port to C++ from public domain IronDoc C sources.
+// Note many code comments here come verbatim from cut-and-pasted IronDoc.
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+
+class morkHashArrays {
+public:
+  nsIMdbHeap*   mHashArrays_Heap;     // copy of mMap_Heap
+  mork_count    mHashArrays_Slots;    // copy of mMap_Slots
+  
+  mork_u1*      mHashArrays_Keys;     // copy of mMap_Keys
+  mork_u1*      mHashArrays_Vals;     // copy of mMap_Vals
+  morkAssoc*    mHashArrays_Assocs;   // copy of mMap_Assocs
+  mork_change*  mHashArrays_Changes;  // copy of mMap_Changes
+  morkAssoc**   mHashArrays_Buckets;  // copy of mMap_Buckets
+  morkAssoc*    mHashArrays_FreeList; // copy of mMap_FreeList
+  
+public:
+  void finalize(morkEnv* ev);
+};
+
+void morkHashArrays::finalize(morkEnv* ev)
+{
+  nsIMdbEnv* menv = ev->AsMdbEnv();
+  nsIMdbHeap* heap = mHashArrays_Heap;
+  
+  if ( heap )
+  {
+    if ( mHashArrays_Keys )
+      heap->Free(menv, mHashArrays_Keys);
+    if ( mHashArrays_Vals )
+      heap->Free(menv, mHashArrays_Vals);
+    if ( mHashArrays_Assocs )
+      heap->Free(menv, mHashArrays_Assocs);
+    if ( mHashArrays_Changes )
+      heap->Free(menv, mHashArrays_Changes);
+    if ( mHashArrays_Buckets )
+      heap->Free(menv, mHashArrays_Buckets);
+  }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkMap::CloseMorkNode(morkEnv* ev) // CloseMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkMap::~morkMap() // assert CloseMap() executed earlier
+{
+  MORK_ASSERT(mMap_FreeList==0);
+  MORK_ASSERT(mMap_Buckets==0);
+  MORK_ASSERT(mMap_Keys==0);
+  MORK_ASSERT(mMap_Vals==0);
+  MORK_ASSERT(mMap_Changes==0);
+  MORK_ASSERT(mMap_Assocs==0);
+}
+
+/*public non-poly*/ void
+morkMap::CloseMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      nsIMdbHeap* heap = mMap_Heap;
+      if ( heap ) /* need to free the arrays? */
+      {
+        nsIMdbEnv* menv = ev->AsMdbEnv();
+        
+        if ( mMap_Keys )
+          heap->Free(menv, mMap_Keys);
+          
+        if ( mMap_Vals )
+          heap->Free(menv, mMap_Vals);
+          
+        if ( mMap_Assocs )
+          heap->Free(menv, mMap_Assocs);
+          
+        if ( mMap_Buckets )
+          heap->Free(menv, mMap_Buckets);
+          
+        if ( mMap_Changes )
+          heap->Free(menv, mMap_Changes);
+      }
+      mMap_Keys = 0;
+      mMap_Vals = 0;
+      mMap_Buckets = 0;
+      mMap_Assocs = 0;
+      mMap_Changes = 0;
+      mMap_FreeList = 0;
+      MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm));
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+void
+morkMap::clear_map(morkEnv* ev, nsIMdbHeap* ioSlotHeap)
+{
+  mMap_Tag = 0;
+  mMap_Seed = 0;
+  mMap_Slots = 0;
+  mMap_Fill = 0;
+  mMap_Keys = 0;
+  mMap_Vals = 0;
+  mMap_Assocs = 0;
+  mMap_Changes = 0;
+  mMap_Buckets = 0;
+  mMap_FreeList = 0;
+  MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm));
+  
+  mMap_Heap = 0;
+  if ( ioSlotHeap )
+  {
+    nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mMap_Heap);
+  }
+  else
+    ev->NilPointerError();
+}
+
+morkMap::morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+  mork_size inKeySize, mork_size inValSize,
+  mork_size inSlots, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges)
+: morkNode(ev, inUsage, ioHeap)
+, mMap_Heap( 0 )
+{
+  if ( ev->Good() )
+  {
+    this->clear_map(ev, ioSlotHeap);
+    if ( ev->Good() )
+    {
+      mMap_Form.mMapForm_HoldChanges = inHoldChanges;
+      
+      mMap_Form.mMapForm_KeySize = inKeySize;
+      mMap_Form.mMapForm_ValSize = inValSize;
+      mMap_Form.mMapForm_KeyIsIP = ( inKeySize == sizeof(mork_ip) );
+      mMap_Form.mMapForm_ValIsIP = ( inValSize == sizeof(mork_ip) );
+      
+      this->InitMap(ev, inSlots);
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kMap;
+    }
+  }
+}
+
+void
+morkMap::NewIterOutOfSyncError(morkEnv* ev)
+{
+  ev->NewError("map iter out of sync");
+}
+
+void morkMap::NewBadMapError(morkEnv* ev)
+{
+  ev->NewError("bad morkMap tag");
+  if ( !this )
+    ev->NewError("nil morkMap instance");
+}
+
+void morkMap::NewSlotsUnderflowWarning(morkEnv* ev)
+{
+  ev->NewWarning("member count underflow");
+}
+
+void morkMap::InitMap(morkEnv* ev, mork_size inSlots)
+{
+  if ( ev->Good() )
+  {
+    morkHashArrays old;
+    // MORK_MEMCPY(&mMap_Form, &inForm, sizeof(morkMapForm));
+    if ( inSlots < 3 ) /* requested capacity absurdly small? */
+      inSlots = 3; /* bump it up to a minimum practical level */
+    else if ( inSlots > (128 * 1024) ) /* requested slots absurdly big? */
+      inSlots = (128 * 1024); /* decrease it to a maximum practical level */
+      
+    if ( this->new_arrays(ev, &old, inSlots) )
+      mMap_Tag = morkMap_kTag;
+
+    MORK_MEMSET(&old, 0, sizeof(morkHashArrays)); // do NOT finalize
+  }
+}
+
+morkAssoc**
+morkMap::find(morkEnv* ev, const void* inKey, mork_u4 inHash) const
+{
+  mork_u1* keys = mMap_Keys;
+  mork_num keySize = this->FormKeySize();
+  // morkMap_mEqual equal = this->FormEqual();
+  
+  morkAssoc** ref = mMap_Buckets + (inHash % mMap_Slots);
+  morkAssoc* assoc = *ref;
+  while ( assoc ) /* look at another assoc in the bucket? */
+  {
+    mork_pos i = assoc - mMap_Assocs; /* index of this assoc */
+    if ( this->Equal(ev, keys + (i * keySize), inKey) ) /* found? */
+      return ref;
+      
+    ref = &assoc->mAssoc_Next; /* consider next assoc slot in bucket */
+    assoc = *ref; /* if this is null, then we are done */
+  }
+  return 0;
+}
+
+/*| get_assoc: read the key and/or value at index inPos
+|*/
+void
+morkMap::get_assoc(void* outKey, void* outVal, mork_pos inPos) const
+{
+  mork_num valSize = this->FormValSize();
+  if ( valSize && outVal ) /* map holds values? caller wants the value? */
+  {
+    const mork_u1* value = mMap_Vals + (valSize * inPos);
+    if ( valSize == sizeof(mork_ip) && this->FormValIsIP() ) /* ip case? */
+      *((mork_ip*) outVal) = *((const mork_ip*) value);
+    else
+      MORK_MEMCPY(outVal, value, valSize);
+  }
+  if ( outKey ) /* caller wants the key? */
+  {
+    mork_num keySize = this->FormKeySize();
+    const mork_u1* key = mMap_Keys + (keySize * inPos);
+    if ( keySize == sizeof(mork_ip) && this->FormKeyIsIP() ) /* ip case? */
+      *((mork_ip*) outKey) = *((const mork_ip*) key);
+    else
+      MORK_MEMCPY(outKey, key, keySize);
+  }
+}
+
+/*| put_assoc: write the key and/or value at index inPos
+|*/
+void
+morkMap::put_assoc(const void* inKey, const void* inVal, mork_pos inPos) const
+{
+  mork_num valSize = this->FormValSize();
+  if ( valSize && inVal ) /* map holds values? caller sends a value? */
+  {
+    mork_u1* value = mMap_Vals + (valSize * inPos);
+    if ( valSize == sizeof(mork_ip) && this->FormValIsIP() ) /* ip case? */
+      *((mork_ip*) value) = *((const mork_ip*) inVal);
+    else
+      MORK_MEMCPY(value, inVal, valSize);
+  }
+  if ( inKey ) /* caller sends a key? */
+  {
+    mork_num keySize = this->FormKeySize();
+    mork_u1* key = mMap_Keys + (keySize * inPos);
+    if ( keySize == sizeof(mork_ip) && this->FormKeyIsIP() ) /* ip case? */
+      *((mork_ip*) key) = *((const mork_ip*) inKey);
+    else
+      MORK_MEMCPY(key, inKey, keySize);
+  }
+}
+
+void*
+morkMap::clear_alloc(morkEnv* ev, mork_size inSize)
+{
+  void* p = 0;
+  nsIMdbHeap* heap = mMap_Heap;
+  if ( heap )
+  {
+    if ( heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p) == 0 && p )
+    {
+      MORK_MEMSET(p, 0, inSize);
+      return p;
+    }
+  }
+  else
+    ev->NilPointerError();
+    
+  return (void*) 0;
+}
+
+void*
+morkMap::alloc(morkEnv* ev, mork_size inSize)
+{
+  void* p = 0;
+  nsIMdbHeap* heap = mMap_Heap;
+  if ( heap )
+  {
+    if ( heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p) == 0 && p )
+      return p;
+  }
+  else
+    ev->NilPointerError();
+
+  return (void*) 0;
+}
+
+/*| new_keys: allocate an array of inSlots new keys filled with zero.
+|*/
+mork_u1*
+morkMap::new_keys(morkEnv* ev, mork_num inSlots)
+{
+  mork_num size = inSlots * this->FormKeySize();
+  return (mork_u1*) this->clear_alloc(ev, size);
+}
+
+/*| new_values: allocate an array of inSlots new values filled with zero.
+**| When values are zero sized, we just return a null pointer.
+|*/
+mork_u1*
+morkMap::new_values(morkEnv* ev, mork_num inSlots)
+{
+  mork_u1* values = 0;
+  mork_num size = inSlots * this->FormValSize();
+  if ( size )
+    values = (mork_u1*) this->clear_alloc(ev, size);
+  return values;
+}
+
+mork_change*
+morkMap::new_changes(morkEnv* ev, mork_num inSlots)
+{
+  mork_change* changes = 0;
+  mork_num size = inSlots * sizeof(mork_change);
+  if ( size && mMap_Form.mMapForm_HoldChanges )
+    changes = (mork_change*) this->clear_alloc(ev, size);
+  return changes;
+}
+
+/*| new_buckets: allocate an array of inSlots new buckets filled with zero.
+|*/
+morkAssoc**
+morkMap::new_buckets(morkEnv* ev, mork_num inSlots)
+{
+  mork_num size = inSlots * sizeof(morkAssoc*);
+  return (morkAssoc**) this->clear_alloc(ev, size);
+}
+
+/*| new_assocs: allocate an array of inSlots new assocs, with each assoc
+**| linked together in a list with the first array element at the list head
+**| and the last element at the list tail. (morkMap::grow() needs this.)
+|*/
+morkAssoc*
+morkMap::new_assocs(morkEnv* ev, mork_num inSlots)
+{
+  mork_num size = inSlots * sizeof(morkAssoc);
+  morkAssoc* assocs = (morkAssoc*) this->alloc(ev, size);
+  if ( assocs ) /* able to allocate the array? */
+  {
+    morkAssoc* a = assocs + (inSlots - 1); /* the last array element */
+    a->mAssoc_Next = 0; /* terminate tail element of the list with null */
+    while ( --a >= assocs ) /* another assoc to link into the list? */
+      a->mAssoc_Next = a + 1; /* each points to the following assoc */
+  }
+  return assocs;
+}
+
+mork_bool
+morkMap::new_arrays(morkEnv* ev, morkHashArrays* old, mork_num inSlots)
+{
+  mork_bool outNew = morkBool_kFalse;
+    
+  /* see if we can allocate all the new arrays before we go any further: */
+  morkAssoc** newBuckets = this->new_buckets(ev, inSlots);
+  morkAssoc* newAssocs = this->new_assocs(ev, inSlots);
+  mork_u1* newKeys = this->new_keys(ev, inSlots);
+  mork_u1* newValues = this->new_values(ev, inSlots);
+  mork_change* newChanges = this->new_changes(ev, inSlots);
+  
+  /* it is okay for newChanges to be null when changes are not held: */
+  mork_bool okayChanges = ( newChanges || !this->FormHoldChanges() );
+  
+  /* it is okay for newValues to be null when values are zero sized: */
+  mork_bool okayValues = ( newValues || !this->FormValSize() );
+  
+  if ( newBuckets && newAssocs && newKeys && okayChanges && okayValues )
+  {
+    outNew = morkBool_kTrue; /* yes, we created all the arrays we need */
+
+    /* init the old hashArrays with slots from this hash table: */
+    old->mHashArrays_Heap = mMap_Heap;
+    
+    old->mHashArrays_Slots = mMap_Slots;
+    old->mHashArrays_Keys = mMap_Keys;
+    old->mHashArrays_Vals = mMap_Vals;
+    old->mHashArrays_Assocs = mMap_Assocs;
+    old->mHashArrays_Buckets = mMap_Buckets;
+    old->mHashArrays_Changes = mMap_Changes;
+    
+    /* now replace all our array slots with the new structures: */
+    ++mMap_Seed; /* note the map is now changed */
+    mMap_Keys = newKeys;
+    mMap_Vals = newValues;
+    mMap_Buckets = newBuckets;
+    mMap_Assocs = newAssocs;
+    mMap_FreeList = newAssocs; /* all are free to start with */
+    mMap_Changes = newChanges;
+    mMap_Slots = inSlots;
+  }
+  else /* free the partial set of arrays that were actually allocated */
+  {
+    nsIMdbEnv* menv = ev->AsMdbEnv();
+    nsIMdbHeap* heap = mMap_Heap;
+    if ( newBuckets )
+      heap->Free(menv, newBuckets);
+    if ( newAssocs )
+      heap->Free(menv, newAssocs);
+    if ( newKeys )
+      heap->Free(menv, newKeys);
+    if ( newValues )
+      heap->Free(menv, newValues);
+    if ( newChanges )
+      heap->Free(menv, newChanges);
+    
+    MORK_MEMSET(old, 0, sizeof(morkHashArrays));
+  }
+  
+  return outNew;
+}
+
+/*| grow: make the map arrays bigger by 33%.  The old map is completely
+**| full, or else we would not have called grow() to get more space.  This
+**| means the free list is empty, and also means every old key and value is in
+**| use in the old arrays.  So every key and value must be copied to the new
+**| arrays, and since they are contiguous in the old arrays, we can efficiently
+**| bitwise copy them in bulk from the old arrays to the new arrays, without
+**| paying any attention to the structure of the old arrays.
+**|
+**|| The content of the old bucket and assoc arrays need not be copied because
+**| this information is going to be completely rebuilt by rehashing all the
+**| keys into their new buckets, given the new larger map capacity.  The new
+**| bucket array from new_arrays() is assumed to contain all zeroes, which is
+**| necessary to ensure the lists in each bucket stay null terminated as we
+**| build the new linked lists.  (Note no old bucket ordering is preserved.)
+**|
+**|| If the old capacity is N, then in the new arrays the assocs with indexes
+**| from zero to N-1 are still allocated and must be rehashed into the map.
+**| The new free list contains all the following assocs, starting with the new
+**| assoc link at index N.  (We call the links in the link array "assocs"
+**| because allocating a link is the same as allocating the key/value pair
+**| with the same index as the link.)
+**|
+**|| The new free list is initialized simply by pointing at the first new link
+**| in the assoc array after the size of the old assoc array.  This assumes
+**| that FeHashTable_new_arrays() has already linked all the new assocs into a
+**| list with the first at the head of the list and the last at the tail of the
+**| list.  So by making the free list point to the first of the new uncopied
+**| assocs, the list is already well-formed.
+|*/
+mork_bool morkMap::grow(morkEnv* ev)
+{
+  if ( mMap_Heap ) /* can we grow the map? */
+  {
+    mork_num newSlots = (mMap_Slots * 2); /* +100% */
+    morkHashArrays old; /* a place to temporarily hold all the old arrays */
+    if ( this->new_arrays(ev, &old, newSlots) ) /* have more? */
+    {
+      // morkMap_mHash hash = this->FormHash(); /* for terse loop use */
+      
+      /* figure out the bulk volume sizes of old keys and values to move: */
+      mork_num oldSlots = old.mHashArrays_Slots; /* number of old assocs */
+      mork_num keyBulk = oldSlots * this->FormKeySize(); /* key volume */
+      mork_num valBulk = oldSlots * this->FormValSize(); /* values */
+      
+      /* convenient variables for new arrays that need to be rehashed: */
+      morkAssoc** newBuckets = mMap_Buckets; /* new all zeroes */
+      morkAssoc* newAssocs = mMap_Assocs; /* hash into buckets */
+      morkAssoc* newFreeList = newAssocs + oldSlots; /* new room is free */
+      mork_u1* key = mMap_Keys; /* the first key to rehash */
+      --newAssocs; /* back up one before preincrement used in while loop */
+      
+      /* move all old keys and values to the new arrays: */
+      MORK_MEMCPY(mMap_Keys, old.mHashArrays_Keys, keyBulk);
+      if ( valBulk ) /* are values nonzero sized? */
+        MORK_MEMCPY(mMap_Vals, old.mHashArrays_Vals, valBulk);
+        
+      mMap_FreeList = newFreeList; /* remaining assocs are free */
+      
+      while ( ++newAssocs < newFreeList ) /* rehash another old assoc? */
+      {
+        morkAssoc** top = newBuckets + (this->Hash(ev, key) % newSlots);
+        key += this->FormKeySize(); /* the next key to rehash */
+        newAssocs->mAssoc_Next = *top; /* link to prev bucket top */
+        *top = newAssocs; /* push assoc on top of bucket */
+      }
+      ++mMap_Seed; /* note the map has changed */
+      old.finalize(ev); /* remember to free the old arrays */
+    }
+  }
+  else ev->OutOfMemoryError();
+  
+  return ev->Good();
+}
+
+
+mork_bool
+morkMap::Put(morkEnv* ev, const void* inKey, const void* inVal,
+  void* outKey, void* outVal, mork_change** outChange)
+{
+  mork_bool outPut = morkBool_kFalse;
+  
+  if ( this->GoodMap() ) /* looks good? */
+  {
+    mork_u4 hash = this->Hash(ev, inKey);
+    morkAssoc** ref = this->find(ev, inKey, hash);
+    if ( ref ) /* assoc was found? reuse an existing assoc slot? */
+    {
+      outPut = morkBool_kTrue; /* inKey was indeed already inside the map */
+    }
+    else /* assoc not found -- need to allocate a new assoc slot */
+    {
+      morkAssoc* assoc = this->pop_free_assoc();
+      if ( !assoc ) /* no slots remaining in free list? must grow map? */
+      {
+        if ( this->grow(ev) ) /* successfully made map larger? */
+          assoc = this->pop_free_assoc();
+      }
+      if ( assoc ) /* allocated new assoc without error? */
+      {
+        ref = mMap_Buckets + (hash % mMap_Slots);
+        assoc->mAssoc_Next = *ref; /* link to prev bucket top */
+        *ref = assoc; /* push assoc on top of bucket */
+          
+        ++mMap_Fill; /* one more member in the collection */
+        ++mMap_Seed; /* note the map has changed */
+      }
+    }
+    if ( ref ) /* did not have an error during possible growth? */
+    {
+      mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */
+      if ( outPut && (outKey || outVal) ) /* copy old before cobbering? */
+        this->get_assoc(outKey, outVal, i);
+
+      this->put_assoc(inKey, inVal, i);
+      ++mMap_Seed; /* note the map has changed */
+      
+      if ( outChange )
+      {
+        if ( mMap_Changes )
+          *outChange = mMap_Changes + i;
+        else
+          *outChange = this->FormDummyChange();
+      }
+    }
+  }
+  else this->NewBadMapError(ev);
+  
+  return outPut;
+}
+
+mork_num
+morkMap::CutAll(morkEnv* ev)
+{
+  mork_num outCutAll = 0;
+  
+  if ( this->GoodMap() ) /* map looks good? */
+  {
+    mork_num slots = mMap_Slots;
+    morkAssoc* before = mMap_Assocs - 1; /* before first member */
+    morkAssoc* assoc = before + slots; /* the very last member */
+
+    ++mMap_Seed; /* note the map is changed */
+
+    /* make the assoc array a linked list headed by first & tailed by last: */
+    assoc->mAssoc_Next = 0; /* null terminate the new free list */
+    while ( --assoc > before ) /* another assoc to link into the list? */
+      assoc->mAssoc_Next = assoc + 1;
+    mMap_FreeList = mMap_Assocs; /* all are free */
+
+    outCutAll = mMap_Fill; /* we'll cut all of them of course */
+
+    mMap_Fill = 0; /* the map is completely empty */
+  }
+  else this->NewBadMapError(ev);
+  
+  return outCutAll;
+}
+
+mork_bool
+morkMap::Cut(morkEnv* ev, const void* inKey,
+  void* outKey, void* outVal, mork_change** outChange)
+{
+  mork_bool outCut = morkBool_kFalse;
+  
+  if ( this->GoodMap() ) /* looks good? */
+  {
+    mork_u4 hash = this->Hash(ev, inKey);
+    morkAssoc** ref = this->find(ev, inKey, hash);
+    if ( ref ) /* found an assoc for key? */
+    {
+      outCut = morkBool_kTrue;
+      morkAssoc* assoc = *ref;
+      mork_pos i = assoc - mMap_Assocs; /* index of assoc */
+      if ( outKey || outVal )
+        this->get_assoc(outKey, outVal, i);
+
+      *ref = assoc->mAssoc_Next; /* unlink the found assoc */
+      this->push_free_assoc(assoc); /* and put it in free list */
+
+      if ( outChange )
+      {
+        if ( mMap_Changes )
+          *outChange = mMap_Changes + i;
+        else
+          *outChange = this->FormDummyChange();
+      }
+      
+      ++mMap_Seed; /* note the map has changed */
+      if ( mMap_Fill ) /* the count shows nonzero members? */
+        --mMap_Fill; /* one less member in the collection */
+      else
+        this->NewSlotsUnderflowWarning(ev);
+    }
+  }
+  else this->NewBadMapError(ev);
+  
+  return outCut;
+}
+
+mork_bool
+morkMap::Get(morkEnv* ev, const void* inKey,
+  void* outKey, void* outVal, mork_change** outChange)
+{
+  mork_bool outGet = morkBool_kFalse;
+  
+  if ( this->GoodMap() ) /* looks good? */
+  {
+    mork_u4 hash = this->Hash(ev, inKey);
+    morkAssoc** ref = this->find(ev, inKey, hash);
+    if ( ref ) /* found an assoc for inKey? */
+    {
+      mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */
+      outGet = morkBool_kTrue;
+      this->get_assoc(outKey, outVal, i);
+      if ( outChange )
+      {
+        if ( mMap_Changes )
+          *outChange = mMap_Changes + i;
+        else
+          *outChange = this->FormDummyChange();
+      }
+    }
+  }
+  else this->NewBadMapError(ev);
+  
+  return outGet;
+}
+
+
+morkMapIter::morkMapIter( )
+: mMapIter_Map( 0 )
+, mMapIter_Seed( 0 )
+  
+, mMapIter_Bucket( 0 )
+, mMapIter_AssocRef( 0 )
+, mMapIter_Assoc( 0 )
+, mMapIter_Next( 0 )
+{
+}
+
+void
+morkMapIter::InitMapIter(morkEnv* ev, morkMap* ioMap)
+{
+  mMapIter_Map = 0;
+  mMapIter_Seed = 0;
+
+  mMapIter_Bucket = 0;
+  mMapIter_AssocRef = 0;
+  mMapIter_Assoc = 0;
+  mMapIter_Next = 0;
+
+  if ( ioMap )
+  {
+    if ( ioMap->GoodMap() )
+    {
+      mMapIter_Map = ioMap;
+      mMapIter_Seed = ioMap->mMap_Seed;
+    }
+    else ioMap->NewBadMapError(ev);
+  }
+  else ev->NilPointerError();
+}
+
+morkMapIter::morkMapIter(morkEnv* ev, morkMap* ioMap)
+: mMapIter_Map( 0 )
+, mMapIter_Seed( 0 )
+  
+, mMapIter_Bucket( 0 )
+, mMapIter_AssocRef( 0 )
+, mMapIter_Assoc( 0 )
+, mMapIter_Next( 0 )
+{
+  if ( ioMap )
+  {
+    if ( ioMap->GoodMap() )
+    {
+      mMapIter_Map = ioMap;
+      mMapIter_Seed = ioMap->mMap_Seed;
+    }
+    else ioMap->NewBadMapError(ev);
+  }
+  else ev->NilPointerError();
+}
+
+void
+morkMapIter::CloseMapIter(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+  mMapIter_Map = 0;
+  mMapIter_Seed = 0;
+  
+  mMapIter_Bucket = 0;
+  mMapIter_AssocRef = 0;
+  mMapIter_Assoc = 0;
+  mMapIter_Next = 0;
+}
+
+mork_change*
+morkMapIter::First(morkEnv* ev, void* outKey, void* outVal)
+{
+  mork_change* outFirst = 0;
+  
+  morkMap* map = mMapIter_Map;
+  
+  if ( map && map->GoodMap() ) /* map looks good? */
+  {
+    morkAssoc** bucket = map->mMap_Buckets;
+    morkAssoc** end = bucket + map->mMap_Slots; /* one past last */
+ 
+    mMapIter_Seed = map->mMap_Seed; /* sync the seeds */
+   
+    while ( bucket < end ) /* another bucket in which to look for assocs? */
+    {
+      morkAssoc* assoc = *bucket++;
+      if ( assoc ) /* found the first map assoc in use? */
+      {
+        mork_pos i = assoc - map->mMap_Assocs;
+        mork_change* c = map->mMap_Changes;
+        outFirst = ( c )? (c + i) : map->FormDummyChange();
+        
+        mMapIter_Assoc = assoc; /* current assoc in iteration */
+        mMapIter_Next = assoc->mAssoc_Next; /* more in bucket */
+        mMapIter_Bucket = --bucket; /* bucket for this assoc */
+        mMapIter_AssocRef = bucket; /* slot referencing assoc */
+        
+        map->get_assoc(outKey, outVal, i);
+          
+        break; /* end while loop */
+      }
+    }
+  }
+  else map->NewBadMapError(ev);
+
+  return outFirst;
+}
+
+mork_change*
+morkMapIter::Next(morkEnv* ev, void* outKey, void* outVal)
+{
+  mork_change* outNext = 0;
+  
+  morkMap* map = mMapIter_Map;
+  
+  if ( map && map->GoodMap() ) /* map looks good? */
+  {
+    if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */
+    {
+      morkAssoc* here = mMapIter_Assoc; /* current assoc */
+      if ( here ) /* iteration is not yet concluded? */
+      {
+        morkAssoc* next = mMapIter_Next;
+        morkAssoc* assoc = next; /* default new mMapIter_Assoc */  
+        if ( next ) /* there are more assocs in the same bucket after Here? */
+        {
+          morkAssoc** ref = mMapIter_AssocRef;
+          
+          /* (*HereRef) equals Here, except when Here has been cut, after
+          ** which (*HereRef) always equals Next.  So if (*HereRef) is not
+          ** equal to Next, then HereRef still needs to be updated to point
+          ** somewhere else other than Here.  Otherwise it is fine.
+          */
+          if ( *ref != next ) /* Here was not cut? must update HereRef? */
+            mMapIter_AssocRef = &here->mAssoc_Next;
+            
+          mMapIter_Next = next->mAssoc_Next;
+        }
+        else /* look for the next assoc in the next nonempty bucket */
+        {
+          morkAssoc** bucket = map->mMap_Buckets;
+          morkAssoc** end = bucket + map->mMap_Slots; /* beyond */
+          mMapIter_Assoc = 0; /* default to no more assocs */
+          bucket = mMapIter_Bucket; /* last exhausted bucket */
+          mMapIter_Assoc = 0; /* default to iteration ended */
+          
+          while ( ++bucket < end ) /* another bucket to search for assocs? */
+          {
+            assoc = *bucket;
+            if ( assoc ) /* found another map assoc in use? */
+            {
+              mMapIter_Bucket = bucket;
+              mMapIter_AssocRef = bucket; /* ref to assoc */
+              mMapIter_Next = assoc->mAssoc_Next; /* more */
+              
+              break; /* end while loop */
+            }
+          }
+        }
+        if ( assoc ) /* did we find another assoc in the iteration? */
+        {
+          mMapIter_Assoc = assoc; /* current assoc */
+          mork_pos i = assoc - map->mMap_Assocs;
+          mork_change* c = map->mMap_Changes;
+          outNext = ( c )? (c + i) : map->FormDummyChange();
+        
+          map->get_assoc( outKey, outVal, i);
+        }
+      }
+    }
+    else map->NewIterOutOfSyncError(ev);
+  }
+  else map->NewBadMapError(ev);
+  
+  return outNext;
+}
+
+mork_change*
+morkMapIter::Here(morkEnv* ev, void* outKey, void* outVal)
+{
+  mork_change* outHere = 0;
+  
+  morkMap* map = mMapIter_Map;
+  
+  if ( map && map->GoodMap() ) /* map looks good? */
+  {
+    if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */
+    {
+      morkAssoc* here = mMapIter_Assoc; /* current assoc */
+      if ( here ) /* iteration is not yet concluded? */
+      {
+        mork_pos i = here - map->mMap_Assocs;
+        mork_change* c = map->mMap_Changes;
+        outHere = ( c )? (c + i) : map->FormDummyChange();
+          
+        map->get_assoc(outKey, outVal, i);
+      }
+    }
+    else map->NewIterOutOfSyncError(ev);
+  }
+  else map->NewBadMapError(ev);
+  
+  return outHere;
+}
+
+mork_change*
+morkMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal)
+{
+  mork_change* outCutHere = 0;
+  morkMap* map = mMapIter_Map;
+  
+  if ( map && map->GoodMap() ) /* map looks good? */
+  {
+    if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */
+    {
+      morkAssoc* here = mMapIter_Assoc; /* current assoc */
+      if ( here ) /* iteration is not yet concluded? */
+      {
+        morkAssoc** ref = mMapIter_AssocRef;
+        if ( *ref != mMapIter_Next ) /* not already cut? */
+        {
+          mork_pos i = here - map->mMap_Assocs;
+          mork_change* c = map->mMap_Changes;
+          outCutHere = ( c )? (c + i) : map->FormDummyChange();
+          if ( outKey || outVal )
+            map->get_assoc(outKey, outVal, i);
+            
+          map->push_free_assoc(here); /* add to free list */
+          *ref = mMapIter_Next; /* unlink here from bucket list */
+          
+          /* note the map has changed, but we are still in sync: */
+          mMapIter_Seed = ++map->mMap_Seed; /* sync */
+          
+          if ( map->mMap_Fill ) /* still has nonzero value? */
+            --map->mMap_Fill; /* one less member in the collection */
+          else
+            map->NewSlotsUnderflowWarning(ev);
+        }
+      }
+    }
+    else map->NewIterOutOfSyncError(ev);
+  }
+  else map->NewBadMapError(ev);
+  
+  return outCutHere;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkMap.h
@@ -0,0 +1,394 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKMAP_
+#define _MORKMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/* (These hash methods closely resemble those in public domain IronDoc.) */
+
+/*| Equal: equal for hash table. Note equal(a,b) implies hash(a)==hash(b).
+|*/
+typedef mork_bool (* morkMap_mEqual)
+(const morkMap* self, morkEnv* ev, const void* inKeyA, const void* inKeyB);
+
+/*| Hash: hash for hash table. Note equal(a,b) implies hash(a)==hash(b).
+|*/
+typedef mork_u4 (* morkMap_mHash)
+(const morkMap* self, morkEnv* ev, const void* inKey);
+
+/*| IsNil: whether a key slot contains a "null" value denoting "no such key".
+|*/
+typedef mork_bool (* morkMap_mIsNil)
+(const morkMap* self, morkEnv* ev, const void* inKey);
+
+/*| Note: notify regarding a refcounting change for a key or a value.
+|*/
+//typedef void (* morkMap_mNote)
+//(morkMap* self, morkEnv* ev, void* inKeyOrVal);
+
+/*| morkMapForm: slots need to initialize a new dict.  (This is very similar
+**| to the config object for public domain IronDoc hash tables.)
+|*/
+class morkMapForm { // a struct of callback method pointers for morkMap
+public:
+  
+  // const void*     mMapForm_NilKey;  // externally defined 'nil' bit pattern
+
+  // void*           mMapForm_NilBuf[ 8 ]; // potential place to put NilKey
+  // If keys are no larger than 8*sizeof(void*), NilKey can be put in NilBuf.
+  // Note this should be true for all Mork subclasses, and we plan usage so.
+  
+  // These three methods must always be provided, so zero will cause errors:
+  
+  // morkMap_mEqual  mMapForm_Equal;   // for comparing two keys for identity
+  // morkMap_mHash   mMapForm_Hash;    // deterministic key to hash method
+  // morkMap_mIsNil  mMapForm_IsNil;   // to query whether a key equals 'nil'
+  
+  // If any of these method slots are nonzero, then morkMap will call the
+  // appropriate one to notify dict users when a key or value is added or cut.
+  // Presumably a caller wants to know this in order to perform refcounting or
+  // some other kind of memory management.  These methods are definitely only
+  // called when references to keys or values are inserted or removed, and are
+  // never called when the actual number of references does not change (such
+  // as when added keys are already present or cut keys are alreading missing).
+  // 
+  // morkMap_mNote   mMapForm_AddKey;  // if nonzero, notify about add key
+  // morkMap_mNote   mMapForm_CutKey;  // if nonzero, notify about cut key
+  // morkMap_mNote   mMapForm_AddVal;  // if nonzero, notify about add val
+  // morkMap_mNote   mMapForm_CutVal;  // if nonzero, notify about cut val
+  //
+  // These note methods have been removed because it seems difficult to
+  // guarantee suitable alignment of objects passed to notification methods.
+  
+  // Note dict clients should pick key and val sizes that provide whatever
+  // alignment will be required for an array of such keys and values.
+  mork_size       mMapForm_KeySize; // size of every key (cannot be zero)
+  mork_size       mMapForm_ValSize; // size of every val (can indeed be zero)
+  
+  mork_bool       mMapForm_HoldChanges; // support changes array in the map
+  mork_change     mMapForm_DummyChange; // change used for false HoldChanges
+  mork_bool       mMapForm_KeyIsIP;     // key is mork_ip sized
+  mork_bool       mMapForm_ValIsIP;     // key is mork_ip sized
+};
+
+/*| morkAssoc: a canonical association slot in a morkMap.  A single assoc
+**| instance does nothing except point to the next assoc in the same bucket
+**| of a hash table.  Each assoc has only two interesting attributes: 1) the
+**| address of the assoc, and 2) the next assoc in a bucket's list.  The assoc
+**| address is interesting because in the context of an array of such assocs,
+**| one can determine the index of a particular assoc in the array by address
+**| arithmetic, subtracting the array address from the assoc address.  And the
+**| index of each assoc is the same index as the associated key and val slots
+**| in the associated arrays
+**|
+**|| Think of an assoc instance as really also containing a key slot and a val
+**| slot, where each key is mMap_Form.mMapForm_KeySize bytes in size, and
+**| each val is mMap_Form.mMapForm_ValSize in size.  But the key and val
+**| slots are stored in separate arrays with indexes that are parallel to the
+**| indexes in the array of morkAssoc instances.  We have taken the variable
+**| sized slots out of the morkAssoc structure, and put them into parallel
+**| arrays associated with each morkAssoc by array index.  And this leaves us
+**| with only the link field to the next assoc in each assoc instance.
+|*/
+class morkAssoc {
+public:
+  morkAssoc*   mAssoc_Next;
+};
+
+
+#define morkDerived_kMap     /*i*/ 0x4D70 /* ascii 'Mp' */
+
+#define morkMap_kTag     /*i*/ 0x6D4D6150 /* ascii 'mMaP' */
+
+/*| morkMap: a hash table based on the public domain IronDoc hash table
+**| (which is in turn rather like a similar OpenDoc hash table).
+|*/
+class morkMap : public morkNode {
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+  nsIMdbHeap*       mMap_Heap; // strong ref to heap allocating all space
+  mork_u4           mMap_Tag; // must equal morkMap_kTag
+
+  // When a morkMap instance is constructed, the dict form slots must be
+  // provided in order to properly configure a dict with all runtime needs:
+  
+  morkMapForm       mMap_Form; // construction time parameterization
+  
+  // Whenever the dict changes structure in a way that would affect any
+  // iteration of the dict associations, the seed increments to show this:
+  
+  mork_seed         mMap_Seed;   // counter for member and structural changes
+  
+  // The current total assoc capacity of the dict is mMap_Slots, where
+  // mMap_Fill of these slots are actually holding content, so mMap_Fill
+  // is the actual membership count, and mMap_Slots is how larger membership
+  // can become before the hash table must grow the buffers being used. 
+  
+  mork_count        mMap_Slots;  // count of slots in the hash table
+  mork_fill         mMap_Fill;   // number of used slots in the hash table
+  
+  // Key and value slots are bound to corresponding mMap_Assocs array slots.
+  // Instead of having a single array like this: {key,val,next}[ mMap_Slots ]
+  // we have instead three parallel arrays with essentially the same meaning:
+  // {key}[ mMap_Slots ], {val}[ mMap_Slots ], {assocs}[ mMap_Slots ]
+  
+  mork_u1*          mMap_Keys;  // mMap_Slots * mMapForm_KeySize buffer
+  mork_u1*          mMap_Vals;  // mMap_Slots * mMapForm_ValSize buffer
+
+  // An assoc is "used" when it appears in a bucket's linked list of assocs.
+  // Until an assoc is used, it appears in the FreeList linked list.  Every
+  // assoc that becomes used goes into the bucket determined by hashing the
+  // key associated with a new assoc.  The key associated with a new assoc
+  // goes in to the slot in mMap_Keys which occupies exactly the same array
+  // index as the array index of the used assoc in the mMap_Assocs array.
+  
+  morkAssoc*        mMap_Assocs;   // mMap_Slots * sizeof(morkAssoc) buffer
+  
+  // The changes array is only needed when the
+
+  mork_change*      mMap_Changes;  // mMap_Slots * sizeof(mork_change) buffer
+  
+  // The Buckets array need not be the same length as the Assocs array, but we
+  // usually do it that way so the average bucket depth is no more than one.
+  // (We could pick a different policy, or make it parameterizable, but that's
+  // tuning we can do some other time.)
+  
+  morkAssoc**       mMap_Buckets;  // mMap_Slots * sizeof(morkAssoc*) buffer
+  
+  // The length of the mMap_FreeList should equal (mMap_Slots - mMap_Fill).
+  // We need a free list instead of a simpler representation because assocs
+  // can be cut and returned to availability in any kind of unknown pattern.
+  // (However, when assocs are first allocated, or when the dict is grown, we
+  // know all new assocs are contiguous and can chain together adjacently.)
+  
+  morkAssoc*        mMap_FreeList; // list of unused mMap_Assocs array slots 
+
+public: // getters (morkProbeMap compatibility)
+  mork_fill        MapFill() const { return mMap_Fill; }
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseMap() only if open
+  virtual ~morkMap(); // assert that CloseMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, 
+    mork_size inKeySize, mork_size inValSize,
+    mork_size inSlots, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges);
+  
+  void CloseMap(morkEnv* ev); // called by 
+  
+public: // dynamic type identification
+  mork_bool IsMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kMap; }
+// } ===== end morkNode methods =====
+
+public: // poly map hash table methods
+
+// { ===== begin morkMap poly interface =====
+  virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b)
+  Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const = 0;
+
+  virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b)
+  Hash(morkEnv* ev, const void* inKey) const = 0;
+// } ===== end morkMap poly interface =====
+
+public: // open utitity methods
+
+  mork_bool GoodMapTag() const { return mMap_Tag == morkMap_kTag; }
+  mork_bool GoodMap() const
+  { return ( IsNode() && GoodMapTag() ); }
+  
+  void NewIterOutOfSyncError(morkEnv* ev);
+  void NewBadMapError(morkEnv* ev);
+  void NewSlotsUnderflowWarning(morkEnv* ev);
+  void InitMap(morkEnv* ev, mork_size inSlots);
+
+protected: // internal utitity methods
+
+  friend class morkMapIter;
+  void clear_map(morkEnv* ev, nsIMdbHeap* ioHeap);
+
+  void* alloc(morkEnv* ev, mork_size inSize);
+  void* clear_alloc(morkEnv* ev, mork_size inSize);
+  
+  void push_free_assoc(morkAssoc* ioAssoc)
+  {
+    ioAssoc->mAssoc_Next = mMap_FreeList;
+    mMap_FreeList = ioAssoc;
+  }
+  
+  morkAssoc* pop_free_assoc()
+  {
+    morkAssoc* assoc = mMap_FreeList;
+    if ( assoc )
+      mMap_FreeList = assoc->mAssoc_Next;
+    return assoc;
+  }
+
+  morkAssoc** find(morkEnv* ev, const void* inKey, mork_u4 inHash) const;
+  
+  mork_u1* new_keys(morkEnv* ev, mork_num inSlots);
+  mork_u1* new_values(morkEnv* ev, mork_num inSlots);
+  mork_change* new_changes(morkEnv* ev, mork_num inSlots);
+  morkAssoc** new_buckets(morkEnv* ev, mork_num inSlots);
+  morkAssoc* new_assocs(morkEnv* ev, mork_num inSlots);
+  mork_bool new_arrays(morkEnv* ev, morkHashArrays* old, mork_num inSlots);
+  
+  mork_bool grow(morkEnv* ev);
+
+  void get_assoc(void* outKey, void* outVal, mork_pos inPos) const;
+  void put_assoc(const void* inKey, const void* inVal, mork_pos inPos) const;
+  
+public: // inlines to form slots
+  // const void*     FormNilKey() const { return mMap_Form.mMapForm_NilKey; }
+  
+  // morkMap_mEqual  FormEqual() const { return mMap_Form.mMapForm_Equal; }
+  // morkMap_mHash   FormHash() const { return mMap_Form.mMapForm_Hash; }
+  // orkMap_mIsNil  FormIsNil() const { return mMap_Form.mMapForm_IsNil; } 
+    
+  // morkMap_mNote   FormAddKey() const { return mMap_Form.mMapForm_AddKey; }
+  // morkMap_mNote   FormCutKey() const { return mMap_Form.mMapForm_CutKey; }
+  // morkMap_mNote   FormAddVal() const { return mMap_Form.mMapForm_AddVal; }
+  // morkMap_mNote   FormCutVal() const { return mMap_Form.mMapForm_CutVal; }
+  
+  mork_size       FormKeySize() const { return mMap_Form.mMapForm_KeySize; }
+  mork_size       FormValSize() const { return mMap_Form.mMapForm_ValSize; }
+
+  mork_bool       FormKeyIsIP() const { return mMap_Form.mMapForm_KeyIsIP; }
+  mork_bool       FormValIsIP() const { return mMap_Form.mMapForm_ValIsIP; }
+
+  mork_bool       FormHoldChanges() const
+  { return mMap_Form.mMapForm_HoldChanges; }
+  
+  mork_change*    FormDummyChange()
+  { return &mMap_Form.mMapForm_DummyChange; }
+
+public: // other map methods
+ 
+  mork_bool Put(morkEnv* ev, const void* inKey, const void* inVal,
+    void* outKey, void* outVal, mork_change** outChange);
+    
+  mork_bool Cut(morkEnv* ev, const void* inKey,
+    void* outKey, void* outVal, mork_change** outChange);
+    
+  mork_bool Get(morkEnv* ev, const void* inKey, 
+    void* outKey, void* outVal, mork_change** outChange);
+    
+  mork_num CutAll(morkEnv* ev);
+
+private: // copying is not allowed
+  morkMap(const morkMap& other);
+  morkMap& operator=(const morkMap& other);
+
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakMap(morkMap* me,
+    morkEnv* ev, morkMap** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongMap(morkMap* me,
+    morkEnv* ev, morkMap** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+/*| morkMapIter: an iterator for morkMap and subclasses.  This is not a node,
+**| and expected usage is as a member of some other node subclass, such as in
+**| a cursor subclass or a thumb subclass.  Also, iters might be as temp stack
+**| objects when scanning the content of a map.
+|*/
+class morkMapIter{  // iterator for hash table map
+
+protected:
+  morkMap*    mMapIter_Map;      // map to iterate, NOT refcounted
+  mork_seed   mMapIter_Seed;     // cached copy of map's seed
+  
+  morkAssoc** mMapIter_Bucket;   // one bucket in mMap_Buckets array
+  morkAssoc** mMapIter_AssocRef; // usually *AtRef equals Here
+  morkAssoc*  mMapIter_Assoc;    // the current assoc in an iteration
+  morkAssoc*  mMapIter_Next;     // mMapIter_Assoc->mAssoc_Next */
+
+public:
+  morkMapIter(morkEnv* ev, morkMap* ioMap);
+  void CloseMapIter(morkEnv* ev);
+ 
+  morkMapIter( ); // everything set to zero -- need to call InitMapIter()
+
+protected: // we want all subclasses to provide typesafe wrappers:
+
+  void InitMapIter(morkEnv* ev, morkMap* ioMap);
+ 
+  // The morkAssoc returned below is always either mork_change* or
+  // else nil (when there is no such assoc).  We return a pointer to
+  // the change rather than a simple bool, because callers might
+  // want to access change info associated with an assoc.
+  
+  mork_change* First(morkEnv* ev, void* outKey, void* outVal);
+  mork_change* Next(morkEnv* ev, void* outKey, void* outVal);
+  mork_change* Here(morkEnv* ev, void* outKey, void* outVal);
+  
+  mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKMAP_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkNode.cpp
@@ -0,0 +1,687 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKHANDLE_
+#include "morkHandle.h"
+#endif
+
+/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
+
+/* ===== ===== ===== ===== morkUsage ===== ===== ===== ===== */
+
+static morkUsage morkUsage_gHeap; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kHeap = morkUsage_gHeap;
+
+static morkUsage morkUsage_gStack; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kStack = morkUsage_gStack;
+
+static morkUsage morkUsage_gMember; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kMember = morkUsage_gMember;
+
+static morkUsage morkUsage_gGlobal; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kGlobal = morkUsage_gGlobal;
+
+static morkUsage morkUsage_gPool; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kPool = morkUsage_gPool;
+
+static morkUsage morkUsage_gNone; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kNone = morkUsage_gNone;
+
+// This must be structured to allow for non-zero values in global variables
+// just before static init time.  We can only safely check for whether a
+// global has the address of some other global.  Please, do not initialize
+// either of the variables below to zero, because this could break when a zero
+// is assigned at static init time, but after EnsureReadyStaticUsage() runs.
+
+static mork_u4 morkUsage_g_static_init_target; // only address of this matters
+static mork_u4* morkUsage_g_static_init_done; // is address of target above?
+
+#define morkUsage_do_static_init() \
+  ( morkUsage_g_static_init_done = &morkUsage_g_static_init_target )
+
+#define morkUsage_need_static_init() \
+  ( morkUsage_g_static_init_done != &morkUsage_g_static_init_target )
+
+/*static*/
+void morkUsage::EnsureReadyStaticUsage()
+{
+  if ( morkUsage_need_static_init() )
+  {
+    morkUsage_do_static_init();
+
+    morkUsage_gHeap.InitUsage(morkUsage_kHeap);
+    morkUsage_gStack.InitUsage(morkUsage_kStack);
+    morkUsage_gMember.InitUsage(morkUsage_kMember);
+    morkUsage_gGlobal.InitUsage(morkUsage_kGlobal);
+    morkUsage_gPool.InitUsage(morkUsage_kPool);
+    morkUsage_gNone.InitUsage(morkUsage_kNone);
+  }
+}
+
+/*static*/
+const morkUsage& morkUsage::GetHeap()   // kHeap safe at static init time
+{
+  EnsureReadyStaticUsage();
+  return morkUsage_gHeap;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetStack()  // kStack safe at static init time
+{
+  EnsureReadyStaticUsage();
+  return morkUsage_gStack;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetMember() // kMember safe at static init time
+{
+  EnsureReadyStaticUsage();
+  return morkUsage_gMember;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetGlobal() // kGlobal safe at static init time
+{
+  EnsureReadyStaticUsage();
+  return morkUsage_gGlobal;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetPool()  // kPool safe at static init time
+{
+  EnsureReadyStaticUsage();
+  return morkUsage_gPool;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetNone()  // kNone safe at static init time
+{
+  EnsureReadyStaticUsage();
+  return morkUsage_gNone;
+}
+
+morkUsage::morkUsage()
+{
+  if ( morkUsage_need_static_init() )
+  {
+    morkUsage::EnsureReadyStaticUsage();
+  }
+}
+
+morkUsage::morkUsage(mork_usage code)
+  : mUsage_Code(code)
+{
+  if ( morkUsage_need_static_init() )
+  {
+    morkUsage::EnsureReadyStaticUsage();
+  }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*static*/ void*
+morkNode::MakeNew(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev)
+{
+  void* node = 0;
+  if ( &ioHeap )
+  {
+    ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void **) &node);
+    if ( !node )
+      ev->OutOfMemoryError();
+  }
+  else
+    ev->NilPointerError();
+  
+  return node;
+}
+
+/*public non-poly*/ void
+morkNode::ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap)
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mork_usage usage = mNode_Usage; // mNode_Usage before ~morkNode
+      this->morkNode::~morkNode(); // first call polymorphic destructor
+      if ( ioHeap ) // was this node heap allocated?
+        ioHeap->Free(ev->AsMdbEnv(), this);
+      else if ( usage == morkUsage_kPool ) // mNode_Usage before ~morkNode
+      {
+        morkHandle* h = (morkHandle*) this;
+        if ( h->IsHandle() && h->GoodHandleTag() )
+        {
+          if ( h->mHandle_Face )
+          {
+            if (ev->mEnv_HandlePool)
+              ev->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face);
+            else if (h->mHandle_Env && h->mHandle_Env->mEnv_HandlePool)
+              h->mHandle_Env->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face);
+          }
+          else
+            ev->NilPointerError();
+        }
+      }
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+/*public virtual*/ void
+morkNode::CloseMorkNode(morkEnv* ev) // CloseNode() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseNode(ev);
+    this->MarkShut();
+  }
+}
+NS_IMETHODIMP
+morkNode::CloseMdbObject(nsIMdbEnv* mev)
+{
+  return morkNode::CloseMdbObject((morkEnv *) mev);
+}
+
+mdb_err morkNode::CloseMdbObject(morkEnv *ev)
+{
+  // if only one ref, Handle_CutStrongRef will clean up better.
+  if (mNode_Uses == 1)
+    return CutStrongRef(ev);
+
+  mdb_err outErr = 0;
+  
+  if ( IsNode() && IsOpenNode() )
+  {
+    if ( ev )
+    {
+      CloseMorkNode(ev);
+      outErr = ev->AsErr();
+    }
+  }
+  return outErr;
+}
+
+/*public virtual*/
+morkNode::~morkNode() // assert that CloseNode() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode() || IsDeadNode()); // sometimes we call destructor explictly w/o freeing object.
+  mNode_Access = morkAccess_kDead;
+  mNode_Usage = morkUsage_kNone;
+}
+
+/*public virtual*/
+// void CloseMorkNode(morkEnv* ev) = 0; // CloseNode() only if open
+  // CloseMorkNode() is the polymorphic close method called when uses==0,
+  // which must do NOTHING at all when IsOpenNode() is not true.  Otherwise,
+  // CloseMorkNode() should call a static close method specific to an object.
+  // Each such static close method should either call inherited static close
+  // methods, or else perform the consolidated effect of calling them, where
+  // subclasses should closely track any changes in base classes with care.
+  
+
+/*public non-poly*/
+morkNode::morkNode( mork_usage inCode )
+: mNode_Heap( 0 )
+, mNode_Base( morkBase_kNode )
+, mNode_Derived ( 0 ) // until subclass sets appropriately
+, mNode_Access( morkAccess_kOpen )
+, mNode_Usage( inCode )
+, mNode_Mutable( morkAble_kEnabled )
+, mNode_Load( morkLoad_kClean )
+, mNode_Uses( 1 )
+, mNode_Refs( 1 )
+{
+}
+
+/*public non-poly*/
+morkNode::morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+: mNode_Heap( ioHeap )
+, mNode_Base( morkBase_kNode )
+, mNode_Derived ( 0 ) // until subclass sets appropriately
+, mNode_Access( morkAccess_kOpen )
+, mNode_Usage( inUsage.Code() )
+, mNode_Mutable( morkAble_kEnabled )
+, mNode_Load( morkLoad_kClean )
+, mNode_Uses( 1 )
+, mNode_Refs( 1 )
+{
+  if ( !ioHeap && mNode_Usage == morkUsage_kHeap )
+    MORK_ASSERT(ioHeap);
+}
+
+/*public non-poly*/
+morkNode::morkNode(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+: mNode_Heap( ioHeap )
+, mNode_Base( morkBase_kNode )
+, mNode_Derived ( 0 ) // until subclass sets appropriately
+, mNode_Access( morkAccess_kOpen )
+, mNode_Usage( inUsage.Code() )
+, mNode_Mutable( morkAble_kEnabled )
+, mNode_Load( morkLoad_kClean )
+, mNode_Uses( 1 )
+, mNode_Refs( 1 )
+{
+  if ( !ioHeap && mNode_Usage == morkUsage_kHeap )
+  {
+    this->NilHeapError(ev);
+  }
+}
+
+/*protected non-poly*/ void
+morkNode::RefsUnderUsesWarning(morkEnv* ev) const 
+{
+  ev->NewError("mNode_Refs < mNode_Uses");
+}
+
+/*protected non-poly*/ void
+morkNode::NonNodeError(morkEnv* ev) const // called when IsNode() is false
+{
+  ev->NewError("non-morkNode");
+}
+
+/*protected non-poly*/ void
+morkNode::NonOpenNodeError(morkEnv* ev) const // when IsOpenNode() is false
+{
+  ev->NewError("non-open-morkNode");
+}
+
+/*protected non-poly*/ void
+morkNode::NonMutableNodeError(morkEnv* ev) const // when IsMutable() is false
+{
+  ev->NewError("non-mutable-morkNode");
+}
+
+/*protected non-poly*/ void
+morkNode::NilHeapError(morkEnv* ev) const // zero mNode_Heap w/ kHeap usage
+{
+  ev->NewError("nil mNode_Heap");
+}
+
+/*protected non-poly*/ void
+morkNode::RefsOverflowWarning(morkEnv* ev) const // mNode_Refs overflow
+{
+  ev->NewWarning("mNode_Refs overflow");
+}
+
+/*protected non-poly*/ void
+morkNode::UsesOverflowWarning(morkEnv* ev) const // mNode_Uses overflow
+{
+  ev->NewWarning("mNode_Uses overflow");
+}
+
+/*protected non-poly*/ void
+morkNode::RefsUnderflowWarning(morkEnv* ev) const // mNode_Refs underflow
+{
+  ev->NewWarning("mNode_Refs underflow");
+}
+
+/*protected non-poly*/ void
+morkNode::UsesUnderflowWarning(morkEnv* ev) const // mNode_Uses underflow
+{
+  ev->NewWarning("mNode_Uses underflow");
+}
+
+/*public non-poly*/ void
+morkNode::CloseNode(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+      this->MarkShut();
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+
+extern void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbCompare_SlotStrongCompare(nsIMdbCompare* self, morkEnv* ev,
+  nsIMdbCompare** ioSlot)
+  // If *ioSlot is non-nil, that compare is released by CutStrongRef() and
+  // then zeroed out.  Then if self is non-nil, this is acquired by
+  // calling AddStrongRef(), and if the return value shows success,
+  // then self is put into slot *ioSlot.  Note self can be nil, so we take
+  // expression 'nsIMdbCompare_SlotStrongCompare(0, ev, &slot)'.
+{
+  nsIMdbEnv* menv = ev->AsMdbEnv();
+  nsIMdbCompare* compare = *ioSlot;
+  if ( self != compare )
+  {
+    if ( compare )
+    {
+      *ioSlot = 0;
+      compare->CutStrongRef(menv);
+    }
+    if ( self && ev->Good() && (self->AddStrongRef(menv)==0) && ev->Good() )
+      *ioSlot = self;
+  }
+}
+
+
+extern void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot)
+  // If *ioSlot is non-nil, that file is released by CutStrongRef() and
+  // then zeroed out.  Then if self is non-nil, this is acquired by
+  // calling AddStrongRef(), and if the return value shows success,
+  // then self is put into slot *ioSlot.  Note self can be nil, so we take
+  // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'.
+{
+  nsIMdbFile* file = *ioSlot;
+  if ( self != file )
+  {
+    if ( file )
+    {
+      *ioSlot = 0;
+      NS_RELEASE(file);
+    }
+    if ( self && ev->Good() && (NS_ADDREF(self)>=0) && ev->Good() )
+      *ioSlot = self;
+  }
+}
+  
+void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot)
+  // If *ioSlot is non-nil, that heap is released by CutStrongRef() and
+  // then zeroed out.  Then if self is non-nil, self is acquired by
+  // calling AddStrongRef(), and if the return value shows success,
+  // then self is put into slot *ioSlot.  Note self can be nil, so we
+  // permit expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'.
+{
+  nsIMdbEnv* menv = ev->AsMdbEnv();
+  nsIMdbHeap* heap = *ioSlot;
+  if ( self != heap )
+  {
+    if ( heap )
+    {
+      *ioSlot = 0;
+      heap->HeapCutStrongRef(menv);
+    }
+    if ( self && ev->Good() && (self->HeapAddStrongRef(menv)==0) && ev->Good() )
+      *ioSlot = self;
+  }
+}
+
+/*public static*/ void
+morkNode::SlotStrongNode(morkNode* me, morkEnv* ev, morkNode** ioSlot)
+  // If *ioSlot is non-nil, that node is released by CutStrongRef() and
+  // then zeroed out.  Then if me is non-nil, this is acquired by
+  // calling AddStrongRef(), and if positive is returned to show success,
+  // then me is put into slot *ioSlot.  Note me can be nil, so we take
+  // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'.
+{
+  morkNode* node = *ioSlot;
+  if ( me != node )
+  {
+    if ( node )
+    {
+      // what if this nulls out the ev and causes asserts?
+      // can we move this after the CutStrongRef()?
+      *ioSlot = 0;
+      node->CutStrongRef(ev);
+    }
+    if ( me && me->AddStrongRef(ev) )
+      *ioSlot = me;
+  }
+}
+
+/*public static*/ void
+morkNode::SlotWeakNode(morkNode* me, morkEnv* ev, morkNode** ioSlot)
+  // If *ioSlot is non-nil, that node is released by CutWeakRef() and
+  // then zeroed out.  Then if me is non-nil, this is acquired by
+  // calling AddWeakRef(), and if positive is returned to show success,
+  // then me is put into slot *ioSlot.  Note me can be nil, so we
+  // expression 'morkNode::SlotWeakNode((morkNode*) 0, ev, &slot)'.
+{
+  morkNode* node = *ioSlot;
+  if ( me != node )
+  {
+    if ( node )
+    {
+      *ioSlot = 0;
+      node->CutWeakRef(ev);
+    }
+    if ( me && me->AddWeakRef(ev) )
+      *ioSlot = me;
+  }
+}
+
+/*public non-poly*/ mork_uses
+morkNode::AddStrongRef(morkEnv* ev)
+{
+  mork_uses outUses = 0;
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mork_uses uses = mNode_Uses;
+      mork_refs refs = mNode_Refs;
+      if ( refs < uses ) // need to fix broken refs/uses relation?
+      { 
+        this->RefsUnderUsesWarning(ev);
+        mNode_Refs = mNode_Uses = refs = uses;
+      }
+      if ( refs < morkNode_kMaxRefCount ) // not too great?
+      {
+        mNode_Refs = ++refs;
+        mNode_Uses = ++uses;
+      }
+      else
+        this->RefsOverflowWarning(ev);
+
+      outUses = uses;
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+  return outUses;
+}
+
+/*private non-poly*/ mork_bool
+morkNode::cut_use_count(morkEnv* ev) // just one part of CutStrongRef()
+{
+  mork_bool didCut = morkBool_kFalse;
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mork_uses uses = mNode_Uses;
+      if ( uses ) // not yet zero?
+        mNode_Uses = --uses;
+      else
+        this->UsesUnderflowWarning(ev);
+
+      didCut = morkBool_kTrue;
+      if ( !mNode_Uses ) // last use gone? time to close node?
+      {
+        if ( this->IsOpenNode() )
+        {
+          if ( !mNode_Refs ) // no outstanding reference?
+          {
+            this->RefsUnderflowWarning(ev);
+            ++mNode_Refs; // prevent potential crash during close
+          }
+          this->CloseMorkNode(ev); // polymorphic self close
+          // (Note CutNode() is not polymorphic -- so don't call that.)
+        }
+      }
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+  return didCut;
+}
+
+/*public non-poly*/ mork_uses
+morkNode::CutStrongRef(morkEnv* ev)
+{
+  mork_refs outRefs = 0;
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      if ( this->cut_use_count(ev) )
+        outRefs = this->CutWeakRef(ev);
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+  return outRefs;
+}
+
+/*public non-poly*/ mork_refs
+morkNode::AddWeakRef(morkEnv* ev)
+{
+  mork_refs outRefs = 0;
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mork_refs refs = mNode_Refs;
+      if ( refs < morkNode_kMaxRefCount ) // not too great?
+        mNode_Refs = ++refs;
+      else
+        this->RefsOverflowWarning(ev);
+        
+      outRefs = refs;
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+  return outRefs;
+}
+
+/*public non-poly*/ mork_refs
+morkNode::CutWeakRef(morkEnv* ev)
+{
+  mork_refs outRefs = 0;
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mork_uses uses = mNode_Uses;
+      mork_refs refs = mNode_Refs;
+      if ( refs ) // not yet zero?
+        mNode_Refs = --refs;
+      else
+        this->RefsUnderflowWarning(ev);
+
+      if ( refs < uses ) // need to fix broken refs/uses relation?
+      { 
+        this->RefsUnderUsesWarning(ev);
+        mNode_Refs = mNode_Uses = refs = uses;
+      }
+        
+      outRefs = refs;
+      if ( !refs ) // last reference gone? time to destroy node?
+        this->ZapOld(ev, mNode_Heap); // self destroy, use this no longer
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+  return outRefs;
+}
+
+static const char morkNode_kBroken[] = "broken";
+
+/*public non-poly*/ const char*
+morkNode::GetNodeAccessAsString() const // e.g. "open", "shut", etc.
+{
+  const char* outString = morkNode_kBroken;
+  switch( mNode_Access )
+  {
+    case morkAccess_kOpen: outString = "open"; break;
+    case morkAccess_kClosing: outString = "closing"; break;
+    case morkAccess_kShut: outString = "shut"; break;
+    case morkAccess_kDead: outString = "dead"; break;
+  }
+  return outString;
+}
+
+/*public non-poly*/ const char*
+morkNode::GetNodeUsageAsString() const // e.g. "heap", "stack", etc.
+{
+  const char* outString = morkNode_kBroken;
+  switch( mNode_Usage )
+  {
+    case morkUsage_kHeap: outString = "heap"; break;
+    case morkUsage_kStack: outString = "stack"; break;
+    case morkUsage_kMember: outString = "member"; break;
+    case morkUsage_kGlobal: outString = "global"; break;
+    case morkUsage_kPool: outString = "pool"; break;
+    case morkUsage_kNone: outString = "none"; break;
+  }
+  return outString;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkNode.h
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKNODE_
+#define _MORKNODE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkUsage_kHeap    'h'
+#define morkUsage_kStack   's'
+#define morkUsage_kMember  'm'
+#define morkUsage_kGlobal  'g'
+#define morkUsage_kPool    'p'
+#define morkUsage_kNone    'n'
+
+class morkUsage {
+public:
+  mork_usage     mUsage_Code;  // kHeap, kStack, kMember, or kGhost
+  
+public:
+  morkUsage(mork_usage inCode);
+
+  morkUsage(); // does nothing except maybe call EnsureReadyStaticUsage()
+  void InitUsage( mork_usage inCode)
+  { mUsage_Code = inCode; }
+  
+  ~morkUsage() { }
+  mork_usage Code() const { return mUsage_Code; }
+  
+  static void EnsureReadyStaticUsage();
+  
+public:
+  static const morkUsage& kHeap;      // morkUsage_kHeap
+  static const morkUsage& kStack;     // morkUsage_kStack
+  static const morkUsage& kMember;    // morkUsage_kMember
+  static const morkUsage& kGlobal;    // morkUsage_kGlobal
+  static const morkUsage& kPool;      // morkUsage_kPool
+  static const morkUsage& kNone;      // morkUsage_kNone
+  
+  static const morkUsage& GetHeap();   // kHeap, safe at static init time
+  static const morkUsage& GetStack();  // kStack, safe at static init time
+  static const morkUsage& GetMember(); // kMember, safe at static init time
+  static const morkUsage& GetGlobal(); // kGlobal, safe at static init time
+  static const morkUsage& GetPool();   // kPool, safe at static init time
+  static const morkUsage& GetNone();   // kNone, safe at static init time
+    
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkNode_kMaxRefCount 0x0FFFF /* count sticks if it hits this */
+
+#define morkBase_kNode      /*i*/ 0x4E64 /* ascii 'Nd' */
+
+/*| morkNode: several groups of two-byte integers that track the basic
+**| status of an object that can be used to compose in-memory graphs.
+**| This is the base class for nsIMdbObject (which adds members that fit
+**| the needs of an nsIMdbObject subclass).  The morkNode class is also used
+**| as the base class for other Mork db classes with no strong relation to
+**| the MDB class hierarchy.
+**|
+**|| Heap: the heap in which this node was allocated, when the usage equals
+**| morkUsage_kHeap to show dynamic allocation.  Note this heap is NOT ref-
+**| counted, because that would be too great and complex a burden for all
+**| the nodes allocated in that heap.  So heap users should take care to
+**| understand that nodes allocated in that heap are considered protected
+**| by some inclusive context in which all those nodes are allocated, and
+**| that context must maintain at least one strong refcount for the heap.
+**| Occasionally a node subclass will indeed wish to hold a refcounted
+**| reference to a heap, and possibly the same heap that is in mNode_Heap,
+**| but this is always done in a separate slot that explicitly refcounts,
+**| so we avoid confusing what is meant by the mNode_Heap slot.
+|*/
+class morkNode /*: public nsISupports */ { // base class for constructing Mork object graphs
+
+public: // state is public because the entire Mork system is private
+  
+//  NS_DECL_ISUPPORTS;
+  nsIMdbHeap*    mNode_Heap;     // NON-refcounted heap pointer
+
+  mork_base      mNode_Base;     // must equal morkBase_kNode
+  mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  mork_able      mNode_Mutable;  // can this node be modified?
+  mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  mork_uses      mNode_Uses;     // refcount for strong refs
+  mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+  
+protected: // special case empty construction for morkHandleFrame
+  friend class morkHandleFrame;
+  morkNode() { }
+  
+public: // inlines for weird mNode_Mutable and mNode_Load constants
+
+  void SetFrozen() { mNode_Mutable = morkAble_kDisabled; }
+  void SetMutable() { mNode_Mutable = morkAble_kEnabled; }
+  void SetAsleep() { mNode_Mutable = morkAble_kAsleep; }
+  
+  mork_bool IsFrozen() const { return mNode_Mutable == morkAble_kDisabled; }
+  mork_bool IsMutable() const { return mNode_Mutable == morkAble_kEnabled; }
+  mork_bool IsAsleep() const { return mNode_Mutable == morkAble_kAsleep; }
+
+  void SetNodeClean() { mNode_Load = morkLoad_kClean; }
+  void SetNodeDirty() { mNode_Load = morkLoad_kDirty; }
+  
+  mork_bool IsNodeClean() const { return mNode_Load == morkLoad_kClean; }
+  mork_bool IsNodeDirty() const { return mNode_Load == morkLoad_kDirty; }
+  
+public: // morkNode memory management methods
+  static void* MakeNew(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev);
+  
+  void ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap); // replaces operator delete()
+  // this->morkNode::~morkNode(); // first call polymorphic destructor
+  // if ( ioHeap ) // was this node heap allocated?
+  //    ioHeap->Free(ev->AsMdbEnv(), this);
+
+public: // morkNode memory management operators
+  void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+  { return morkNode::MakeNew(inSize, ioHeap, ev); }
+  
+
+protected: // construction without an anv needed for first env constructed:
+  morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+
+  morkNode(mork_usage inCode); // usage == inCode, heap == nil
+
+// { ===== begin basic node interface =====
+public: // morkNode virtual methods
+  // virtual FlushMorkNode(morkEnv* ev, morkStream* ioStream);
+  // virtual WriteMorkNode(morkEnv* ev, morkStream* ioStream);
+  
+  virtual ~morkNode(); // assert that CloseNode() executed earlier
+  virtual void CloseMorkNode(morkEnv* ev); // CloseNode() only if open
+  
+  // CloseMorkNode() is the polymorphic close method called when uses==0,
+  // which must do NOTHING at all when IsOpenNode() is not true.  Otherwise,
+  // CloseMorkNode() should call a static close method specific to an object.
+  // Each such static close method should either call inherited static close
+  // methods, or else perform the consolidated effect of calling them, where
+  // subclasses should closely track any changes in base classes with care.
+  
+public: // morkNode construction
+  morkNode(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+  void CloseNode(morkEnv* ev); // called by CloseMorkNode();
+  mdb_err CloseMdbObject(morkEnv *ev);
+  NS_IMETHOD CloseMdbObject(nsIMdbEnv *ev);
+private: // copying is not allowed
+  morkNode(const morkNode& other);
+  morkNode& operator=(const morkNode& other);
+
+public: // dynamic type identification
+  mork_bool IsNode() const
+  { return mNode_Base == morkBase_kNode; }
+// } ===== end basic node methods =====
+    
+public: // public error & warning methods
+
+  void RefsUnderUsesWarning(morkEnv* ev) const; // call if mNode_Refs < mNode_Uses
+  void NonNodeError(morkEnv* ev) const; // call when IsNode() is false
+  void NilHeapError(morkEnv* ev) const; // zero mNode_Heap when usage is kHeap
+  void NonOpenNodeError(morkEnv* ev) const; // call when IsOpenNode() is false
+
+  void NonMutableNodeError(morkEnv* ev) const; // when IsMutable() is false
+
+  void RefsOverflowWarning(morkEnv* ev) const; // call on mNode_Refs overflow
+  void UsesOverflowWarning(morkEnv* ev) const; // call on mNode_Uses overflow
+  void RefsUnderflowWarning(morkEnv* ev) const; // call on mNode_Refs underflow
+  void UsesUnderflowWarning(morkEnv* ev) const; // call on mNode_Uses underflow
+
+private: // private refcounting methods
+  mork_bool  cut_use_count(morkEnv* ev); // just one part of CutStrongRef()
+
+public: // other morkNode methods
+
+  mork_bool  GoodRefs() const { return mNode_Refs >= mNode_Uses; }
+  mork_bool  BadRefs() const { return mNode_Refs < mNode_Uses; }
+
+  mork_uses  StrongRefsOnly() const { return mNode_Uses; }
+  mork_refs  WeakRefsOnly() const { return (mork_refs) ( mNode_Refs - mNode_Uses ); }
+
+  // (this refcounting derives from public domain IronDoc node refcounts)
+  virtual mork_refs    AddStrongRef(morkEnv* ev);
+  virtual mork_refs    CutStrongRef(morkEnv* ev);
+  mork_refs    AddWeakRef(morkEnv* ev);
+  mork_refs    CutWeakRef(morkEnv* ev);
+
+  const char* GetNodeAccessAsString() const; // e.g. "open", "shut", etc.
+  const char* GetNodeUsageAsString() const; // e.g. "heap", "stack", etc.
+
+  mork_usage NodeUsage() const { return mNode_Usage; }
+
+  mork_bool IsHeapNode() const
+  { return mNode_Usage == morkUsage_kHeap; }
+
+  mork_bool IsOpenNode() const
+  { return mNode_Access == morkAccess_kOpen; }
+
+  mork_bool IsShutNode() const
+  { return mNode_Access == morkAccess_kShut; }
+
+  mork_bool IsDeadNode() const
+  { return mNode_Access == morkAccess_kDead; }
+
+  mork_bool IsClosingNode() const
+  { return mNode_Access == morkAccess_kClosing; }
+
+  mork_bool IsOpenOrClosingNode() const
+  { return IsOpenNode() || IsClosingNode(); }
+
+  mork_bool HasNodeAccess() const
+  { return ( IsOpenNode() || IsShutNode() || IsClosingNode() ); }
+    
+  void MarkShut() { mNode_Access = morkAccess_kShut; }
+  void MarkClosing() { mNode_Access = morkAccess_kClosing; }
+  void MarkDead() { mNode_Access = morkAccess_kDead; }
+
+public: // refcounting for typesafe subclass inline methods
+  static void SlotWeakNode(morkNode* me, morkEnv* ev, morkNode** ioSlot);
+  // If *ioSlot is non-nil, that node is released by CutWeakRef() and
+  // then zeroed out.  Then if me is non-nil, this is acquired by
+  // calling AddWeakRef(), and if positive is returned to show success,
+  // then this is put into slot *ioSlot.  Note me can be nil, so we
+  // permit expression '((morkNode*) 0L)->SlotWeakNode(ev, &slot)'.
+  
+  static void SlotStrongNode(morkNode* me, morkEnv* ev, morkNode** ioSlot);
+  // If *ioSlot is non-nil, that node is released by CutStrongRef() and
+  // then zeroed out.  Then if me is non-nil, this is acquired by
+  // calling AddStrongRef(), and if positive is returned to show success,
+  // then me is put into slot *ioSlot.  Note me can be nil, so we take
+  // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'.
+};
+
+extern void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot);
+  // If *ioSlot is non-nil, that heap is released by CutStrongRef() and
+  // then zeroed out.  Then if self is non-nil, this is acquired by
+  // calling AddStrongRef(), and if the return value shows success,
+  // then self is put into slot *ioSlot.  Note self can be nil, so we take
+  // expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'.
+
+extern void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot);
+  // If *ioSlot is non-nil, that file is released by CutStrongRef() and
+  // then zeroed out.  Then if self is non-nil, this is acquired by
+  // calling AddStrongRef(), and if the return value shows success,
+  // then self is put into slot *ioSlot.  Note self can be nil, so we take
+  // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'.
+
+extern void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbCompare_SlotStrongCompare(nsIMdbCompare* self, morkEnv* ev,
+  nsIMdbCompare** ioSlot);
+  // If *ioSlot is non-nil, that compare is released by CutStrongRef() and
+  // then zeroed out.  Then if self is non-nil, this is acquired by
+  // calling AddStrongRef(), and if the return value shows success,
+  // then self is put into slot *ioSlot.  Note self can be nil, so we take
+  // expression 'nsIMdbCompare_SlotStrongCompare(0, ev, &slot)'.
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKNODE_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkNodeMap.cpp
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkNodeMap::CloseMorkNode(morkEnv* ev) // CloseNodeMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseNodeMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkNodeMap::~morkNodeMap() // assert CloseNodeMap() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkNodeMap::morkNodeMap(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkIntMap(ev, inUsage, /*valsize*/ sizeof(morkNode*), ioHeap, ioSlotHeap,
+  /*inHoldChanges*/ morkBool_kTrue)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kNodeMap;
+}
+
+/*public non-poly*/ void
+morkNodeMap::CloseNodeMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      this->CutAllNodes(ev);
+      this->CloseMap(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+mork_bool
+morkNodeMap::AddNode(morkEnv* ev, mork_token inToken, morkNode* ioNode)
+  // the AddNode() method return value equals ev->Good().
+{
+  if ( ioNode && ev->Good() )
+  {
+    morkNode* node = 0; // old val in the map
+    
+    mork_bool put = this->Put(ev, &inToken, &ioNode,
+      /*key*/ (void*) 0, &node, (mork_change**) 0);
+      
+    if ( put ) // replaced an existing value for key inToken?
+    {
+      if ( node && node != ioNode ) // need to release old node?
+        node->CutStrongRef(ev);
+    }
+    
+    if ( ev->Bad() || !ioNode->AddStrongRef(ev) )
+    {
+      // problems adding node or increasing refcount?
+      this->Cut(ev, &inToken,  // make sure not in map
+        /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+    }
+  }
+  else if ( !ioNode )
+    ev->NilPointerError();
+    
+  return ev->Good();
+}
+
+mork_bool
+morkNodeMap::CutNode(morkEnv* ev, mork_token inToken)
+{
+  morkNode* node = 0; // old val in the map
+  mork_bool outCutNode = this->Cut(ev, &inToken, 
+    /*key*/ (void*) 0, &node, (mork_change**) 0);
+  if ( node )
+    node->CutStrongRef(ev);
+  
+  return outCutNode;
+}
+
+morkNode*
+morkNodeMap::GetNode(morkEnv* ev, mork_token inToken)
+  // Note the returned node does NOT have an increase in refcount for this.
+{
+  morkNode* node = 0; // old val in the map
+  this->Get(ev, &inToken, /*key*/ (void*) 0, &node, (mork_change**) 0);
+  
+  return node;
+}
+
+mork_num
+morkNodeMap::CutAllNodes(morkEnv* ev)
+  // CutAllNodes() releases all the reference node values.
+{
+  mork_num outSlots = mMap_Slots;
+  mork_token key = 0; // old key token in the map
+  morkNode* val = 0; // old val node in the map
+  
+  mork_change* c = 0;
+  morkNodeMapIter i(ev, this);
+  for ( c = i.FirstNode(ev, &key, &val); c ; c = i.NextNode(ev, &key, &val) )
+  {
+    if ( val )
+      val->CutStrongRef(ev);
+    i.CutHereNode(ev, /*key*/ (mork_token*) 0, /*val*/ (morkNode**) 0);
+  }
+  
+  return outSlots;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkNodeMap.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKNODEMAP_
+#define _MORKNODEMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kNodeMap  /*i*/ 0x6E4D /* ascii 'nM' */
+
+#define morkNodeMap_kStartSlotCount 512
+
+/*| morkNodeMap: maps mork_token -> morkNode
+|*/
+class morkNodeMap : public morkIntMap { // for mapping tokens to nodes
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseNodeMap() only if open
+  virtual ~morkNodeMap(); // assert that CloseNodeMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkNodeMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseNodeMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsNodeMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kNodeMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+  // use the Equal() and Hash() for mork_u4 inherited from morkIntMap
+// } ===== end morkMap poly interface =====
+
+protected: // we want all subclasses to provide typesafe wrappers:
+
+  mork_bool  AddNode(morkEnv* ev, mork_token inToken, morkNode* ioNode);
+  // the AddNode() boolean return equals ev->Good().
+
+  mork_bool  CutNode(morkEnv* ev, mork_token inToken);
+  // The CutNode() boolean return indicates whether removal happened. 
+  
+  morkNode*  GetNode(morkEnv* ev, mork_token inToken);
+  // Note the returned node does NOT have an increase in refcount for this.
+
+  mork_num CutAllNodes(morkEnv* ev);
+  // CutAllNodes() releases all the reference node values.
+};
+
+class morkNodeMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+  morkNodeMapIter(morkEnv* ev, morkNodeMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkNodeMapIter( ) : morkMapIter()  { }
+  void InitNodeMapIter(morkEnv* ev, morkNodeMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change*
+  FirstNode(morkEnv* ev, mork_token* outToken, morkNode** outNode)
+  { return this->First(ev, outToken, outNode); }
+  
+  mork_change*
+  NextNode(morkEnv* ev, mork_token* outToken, morkNode** outNode)
+  { return this->Next(ev, outToken, outNode); }
+  
+  mork_change*
+  HereNode(morkEnv* ev, mork_token* outToken, morkNode** outNode)
+  { return this->Here(ev, outToken, outNode); }
+  
+  mork_change*
+  CutHereNode(morkEnv* ev, mork_token* outToken, morkNode** outNode)
+  { return this->CutHere(ev, outToken, outNode); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKNODEMAP_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkObject.cpp
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKHANDLE_
+#include "morkHandle.h"
+#endif
+
+#include "nsCOMPtr.h"
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+NS_IMPL_ISUPPORTS1(morkObject, nsIMdbObject)
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkObject::CloseMorkNode(morkEnv* ev) // CloseObject() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseObject(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkObject::~morkObject() // assert CloseObject() executed earlier
+{
+  if (!IsShutNode())
+    CloseMorkNode(this->mMorkEnv);
+  MORK_ASSERT(mObject_Handle==0);
+}
+
+/*public non-poly*/
+morkObject::morkObject(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+  mork_color inBeadColor)
+: morkBead(inUsage, ioHeap, inBeadColor)
+, mObject_Handle( 0 )
+{
+  mMorkEnv = nsnull;
+}
+
+/*public non-poly*/
+morkObject::morkObject(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+  mork_color inBeadColor, morkHandle* ioHandle)
+: morkBead(ev, inUsage, ioHeap, inBeadColor)
+, mObject_Handle( 0 )
+{
+  mMorkEnv = ev;
+  if ( ev->Good() )
+  {
+    if ( ioHandle )
+      morkHandle::SlotWeakHandle(ioHandle, ev, &mObject_Handle);
+      
+    if ( ev->Good() )
+      mNode_Derived = morkDerived_kObject;
+  }
+}
+
+/*public non-poly*/ void
+morkObject::CloseObject(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      if ( !this->IsShutNode() )
+      {
+        if ( mObject_Handle )
+          morkHandle::SlotWeakHandle((morkHandle*) 0L, ev, &mObject_Handle);
+          
+        mBead_Color = 0; // this->CloseBead(ev);
+        this->MarkShut();
+      }
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+// { ----- begin factory methods -----
+NS_IMETHODIMP
+morkObject::GetMdbFactory(nsIMdbEnv* mev, nsIMdbFactory** acqFactory)
+{
+  nsresult rv;
+  nsCOMPtr <nsIMdbObject> obj = do_QueryInterface(mev);
+  if (obj)
+    rv = obj->GetMdbFactory(mev, acqFactory);
+  else
+    return NS_ERROR_NO_INTERFACE;
+
+  return rv;
+} 
+// } ----- end factory methods -----
+
+// { ----- begin ref counting for well-behaved cyclic graphs -----
+NS_IMETHODIMP
+morkObject::GetWeakRefCount(nsIMdbEnv* mev, // weak refs
+  mdb_count* outCount)
+{
+  *outCount = WeakRefsOnly();
+  return NS_OK;
+}  
+NS_IMETHODIMP
+morkObject::GetStrongRefCount(nsIMdbEnv* mev, // strong refs
+  mdb_count* outCount)
+{
+  *outCount = StrongRefsOnly();
+  return NS_OK;
+}
+// ### TODO - clean up this cast, if required
+NS_IMETHODIMP
+morkObject::AddWeakRef(nsIMdbEnv* mev)
+{
+  return morkNode::AddWeakRef((morkEnv *) mev);
+}
+NS_IMETHODIMP
+morkObject::AddStrongRef(nsIMdbEnv* mev)
+{
+  return morkNode::AddStrongRef((morkEnv *) mev);
+}
+
+NS_IMETHODIMP
+morkObject::CutWeakRef(nsIMdbEnv* mev)
+{
+  return morkNode::CutWeakRef((morkEnv *) mev);
+}
+NS_IMETHODIMP
+morkObject::CutStrongRef(nsIMdbEnv* mev)
+{
+  return morkNode::CutStrongRef((morkEnv *) mev);
+}
+
+  
+NS_IMETHODIMP
+morkObject::CloseMdbObject(nsIMdbEnv* mev)
+{
+  return morkNode::CloseMdbObject((morkEnv *) mev);
+}
+
+NS_IMETHODIMP
+morkObject::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
+{
+  *outOpen = IsOpenNode();
+  return NS_OK;
+}
+NS_IMETHODIMP
+morkObject::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly)
+{
+  *outIsReadonly = IsFrozen();
+  return NS_OK;
+}
+
+//void morkObject::NewNilHandleError(morkEnv* ev) // mObject_Handle is nil
+//{
+//  ev->NewError("nil mObject_Handle");
+//}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkObject.h
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKOBJECT_
+#define _MORKOBJECT_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKBEAD_
+#include "morkBead.h"
+#endif
+
+#ifndef _MORKCONFIG_
+#include "morkConfig.h"
+#endif
+
+#ifndef _ORKINHEAP_
+#include "orkinHeap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kObject   /*i*/ 0x6F42 /* ascii 'oB' */
+
+/*| morkObject: subclass of morkNode that adds knowledge of db suite factory
+**| and containing port to those objects that are exposed as instances of
+**| nsIMdbObject in the public interface.
+|*/
+class morkObject : public morkBead, public nsIMdbObject { 
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*    mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+  // mork_color      mBead_Color;   // ID for this bead
+  
+public: // state is public because the entire Mork system is private
+
+  morkHandle*      mObject_Handle;   // weak ref to handle for this object
+
+  morkEnv * mMorkEnv; // weak ref to environment this object created in.
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseObject() only if open
+  virtual ~morkObject(); // assert that CloseObject() executed earlier
+#ifdef MORK_DEBUG_HEAP_STATS
+  void operator delete(void* ioAddress, size_t size)
+  { 
+    mork_u4* array = (mork_u4*) ioAddress;
+    array -= 3;
+    orkinHeap *heap = (orkinHeap *) *array;
+    if (heap)
+      heap->Free(nsnull, ioAddress);
+  }
+#endif
+
+  NS_DECL_ISUPPORTS
+
+    // { ----- begin attribute methods -----
+  NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly);
+  // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+  // } ----- end attribute methods -----
+
+  // { ----- begin factory methods -----
+  NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory); 
+  // } ----- end factory methods -----
+
+  // { ----- begin ref counting for well-behaved cyclic graphs -----
+  NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
+    mdb_count* outCount);  
+  NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
+    mdb_count* outCount);
+
+  NS_IMETHOD AddWeakRef(nsIMdbEnv* ev);
+  NS_IMETHOD AddStrongRef(nsIMdbEnv* ev);
+
+  NS_IMETHOD CutWeakRef(nsIMdbEnv* ev);
+  NS_IMETHOD CutStrongRef(nsIMdbEnv* ev);
+  
+  NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev); // called at strong refs zero
+  NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen);
+  // } ----- end ref counting -----
+  
+
+protected: // special case construction of first env without preceding env
+  morkObject(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+    mork_color inBeadColor);
+  
+public: // morkEnv construction & destruction
+  morkObject(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+     mork_color inBeadColor, morkHandle* ioHandle); // ioHandle can be nil
+  void CloseObject(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkObject(const morkObject& other);
+  morkObject& operator=(const morkObject& other);
+
+public: // dynamic type identification
+  mork_bool IsObject() const
+  { return IsNode() && mNode_Derived == morkDerived_kObject; }
+// } ===== end morkNode methods =====
+
+  // void NewNilHandleError(morkEnv* ev); // mObject_Handle is nil
+  
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakObject(morkObject* me,
+    morkEnv* ev, morkObject** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongObject(morkObject* me,
+    morkEnv* ev, morkObject** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKOBJECT_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkParser.cpp
@@ -0,0 +1,1605 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKSINK_
+#include "morkSink.h"
+#endif
+
+#ifndef _MORKCH_
+#include "morkCh.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkParser::CloseMorkNode(morkEnv* ev) // CloseParser() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseParser(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkParser::~morkParser() // assert CloseParser() executed earlier
+{
+  MORK_ASSERT(mParser_Heap==0);
+  MORK_ASSERT(mParser_Stream==0);
+}
+
+/*public non-poly*/
+morkParser::morkParser(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+  morkStream* ioStream, mdb_count inBytesPerParseSegment,
+  nsIMdbHeap* ioSlotHeap)
+: morkNode(ev, inUsage, ioHeap)
+, mParser_Heap( 0 )
+, mParser_Stream( 0 )
+, mParser_MoreGranularity( inBytesPerParseSegment )
+, mParser_State( morkParser_kStartState )
+
+, mParser_GroupContentStartPos( 0 )
+
+, mParser_TableMid(  )
+, mParser_RowMid(  )
+, mParser_CellMid(  )
+    
+, mParser_InPort( morkBool_kFalse )
+, mParser_InDict( morkBool_kFalse )
+, mParser_InCell( morkBool_kFalse )
+, mParser_InMeta( morkBool_kFalse )
+    
+, mParser_InPortRow( morkBool_kFalse )
+, mParser_InRow( morkBool_kFalse )
+, mParser_InTable( morkBool_kFalse )
+, mParser_InGroup( morkBool_kFalse )
+
+, mParser_AtomChange( morkChange_kNil )
+, mParser_CellChange( morkChange_kNil )
+, mParser_RowChange( morkChange_kNil )
+, mParser_TableChange( morkChange_kNil )
+
+, mParser_Change( morkChange_kNil )
+, mParser_IsBroken( morkBool_kFalse )
+, mParser_IsDone( morkBool_kFalse )
+, mParser_DoMore( morkBool_kTrue )
+    
+, mParser_Mid()
+
+, mParser_ScopeCoil(ev, ioSlotHeap)
+, mParser_ValueCoil(ev, ioSlotHeap)
+, mParser_ColumnCoil(ev, ioSlotHeap)
+, mParser_StringCoil(ev, ioSlotHeap)
+
+, mParser_ScopeSpool(ev, &mParser_ScopeCoil)
+, mParser_ValueSpool(ev, &mParser_ValueCoil)
+, mParser_ColumnSpool(ev, &mParser_ColumnCoil)
+, mParser_StringSpool(ev, &mParser_StringCoil)
+
+, mParser_MidYarn(ev, morkUsage_kMember, ioSlotHeap)
+{
+  if ( inBytesPerParseSegment < morkParser_kMinGranularity )
+    inBytesPerParseSegment = morkParser_kMinGranularity;
+  else if ( inBytesPerParseSegment > morkParser_kMaxGranularity )
+    inBytesPerParseSegment = morkParser_kMaxGranularity;
+    
+  mParser_MoreGranularity = inBytesPerParseSegment;
+
+  if ( ioSlotHeap && ioStream )
+  {
+    nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mParser_Heap);
+    morkStream::SlotStrongStream(ioStream, ev, &mParser_Stream);
+    
+    if ( ev->Good() )
+    {
+      mParser_Tag = morkParser_kTag;
+      mNode_Derived = morkDerived_kParser;
+    }
+  }
+  else
+    ev->NilPointerError();
+}
+
+/*public non-poly*/ void
+morkParser::CloseParser(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      if ( !this->IsShutNode() )
+      {
+        mParser_ScopeCoil.CloseCoil(ev);
+        mParser_ValueCoil.CloseCoil(ev);
+        mParser_ColumnCoil.CloseCoil(ev);
+        mParser_StringCoil.CloseCoil(ev);
+        nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mParser_Heap);
+        morkStream::SlotStrongStream((morkStream*) 0, ev, &mParser_Stream);
+        this->MarkShut();
+      }
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*protected non-poly*/ void
+morkParser::NonGoodParserError(morkEnv* ev) // when GoodParserTag() is false
+{
+  ev->NewError("non-morkNode");
+}
+
+/*protected non-poly*/ void
+morkParser::NonUsableParserError(morkEnv* ev) //
+{
+  if ( this->IsNode() )
+  {
+    if ( this->IsOpenNode() )
+    {
+      if (  this->GoodParserTag() )
+      {
+         // okay
+      }
+      else
+        this->NonGoodParserError(ev);
+    }
+    else
+      this->NonOpenNodeError(ev);
+  }
+  else
+    this->NonNodeError(ev);
+}
+
+
+/*protected non-poly*/ void
+morkParser::StartParse(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+  mParser_InCell = morkBool_kFalse;
+  mParser_InMeta = morkBool_kFalse;
+  mParser_InDict = morkBool_kFalse;
+  mParser_InPortRow = morkBool_kFalse;
+  
+  mParser_RowMid.ClearMid();
+  mParser_TableMid.ClearMid();
+  mParser_CellMid.ClearMid();
+  
+  mParser_GroupId = 0;
+  mParser_InPort = morkBool_kTrue;
+
+  mParser_GroupSpan.ClearSpan();
+  mParser_DictSpan.ClearSpan();
+  mParser_AliasSpan.ClearSpan();
+  mParser_MetaSpan.ClearSpan();
+  mParser_TableSpan.ClearSpan();
+  mParser_RowSpan.ClearSpan();
+  mParser_CellSpan.ClearSpan();
+  mParser_ColumnSpan.ClearSpan();
+  mParser_SlotSpan.ClearSpan();
+
+   mParser_PortSpan.ClearSpan();
+}
+
+/*protected non-poly*/ void
+morkParser::StopParse(morkEnv* ev)
+{
+  if ( mParser_InCell )
+  {
+    mParser_InCell = morkBool_kFalse;
+    mParser_CellSpan.SetEndWithEnd(mParser_PortSpan);
+    this->OnCellEnd(ev, mParser_CellSpan);
+  }
+  if ( mParser_InMeta )
+  {
+    mParser_InMeta = morkBool_kFalse;
+    mParser_MetaSpan.SetEndWithEnd(mParser_PortSpan);
+    this->OnMetaEnd(ev, mParser_MetaSpan);
+  }
+  if ( mParser_InDict )
+  {
+    mParser_InDict = morkBool_kFalse;
+    mParser_DictSpan.SetEndWithEnd(mParser_PortSpan);
+    this->OnDictEnd(ev, mParser_DictSpan);
+  }
+  if ( mParser_InPortRow )
+  {
+    mParser_InPortRow = morkBool_kFalse;
+    mParser_RowSpan.SetEndWithEnd(mParser_PortSpan);
+    this->OnPortRowEnd(ev, mParser_RowSpan);
+  }
+  if ( mParser_InRow )
+  {
+    mParser_InRow = morkBool_kFalse;
+    mParser_RowMid.ClearMid();
+    mParser_RowSpan.SetEndWithEnd(mParser_PortSpan);
+    this->OnRowEnd(ev, mParser_RowSpan);
+  }
+  if ( mParser_InTable )
+  {
+    mParser_InTable = morkBool_kFalse;
+    mParser_TableMid.ClearMid();
+    mParser_TableSpan.SetEndWithEnd(mParser_PortSpan);
+    this->OnTableEnd(ev, mParser_TableSpan);
+  }
+  if ( mParser_GroupId )
+  {
+    mParser_GroupId = 0;
+    mParser_GroupSpan.SetEndWithEnd(mParser_PortSpan);
+    this->OnGroupAbortEnd(ev, mParser_GroupSpan);
+  }
+  if ( mParser_InPort )
+  {
+    mParser_InPort = morkBool_kFalse;
+    this->OnPortEnd(ev, mParser_PortSpan);
+  }
+}
+
+int morkParser::eat_comment(morkEnv* ev) // last char was '/'
+{
+  morkStream* s = mParser_Stream;
+  // Note morkStream::Getc() returns EOF when an error occurs, so
+  // we don't need to check for both c != EOF and ev->Good() below.
+  
+  register int c = s->Getc(ev);
+  if ( c == '/' ) // C++ style comment?
+  {
+    while ( (c = s->Getc(ev)) != EOF && c != 0xA && c != 0xD )
+      /* empty */;
+      
+    if ( c == 0xA || c == 0xD )
+      c = this->eat_line_break(ev, c);
+  }
+  else if ( c == '*' ) /* C style comment? */
+  {
+    int depth = 1; // count depth of comments until depth reaches zero
+    
+    while ( depth > 0 && c != EOF ) // still looking for comment end(s)?
+    {
+      while ( (c = s->Getc(ev)) != EOF && c != '/' && c != '*' )
+      {
+        if ( c == 0xA || c == 0xD ) // need to count a line break?
+        {
+          c = this->eat_line_break(ev, c);
+          if ( c == '/' || c == '*' )
+            break; // end while loop
+        }
+      }
+        
+      if ( c == '*' ) // maybe end of a comment, if next char is '/'?
+      {
+        if ( (c = s->Getc(ev)) == '/' ) // end of comment?
+        {
+          --depth; // depth of comments has decreased by one
+          if ( !depth ) // comments all done?
+            c = s->Getc(ev); // return the byte after end of comment
+        }
+        else if ( c != EOF ) // need to put the char back?
+          s->Ungetc(c); // especially need to put back '*', 0xA, or 0xD
+      }
+      else if ( c == '/' ) // maybe nested comemnt, if next char is '*'?
+      {
+        if ( (c = s->Getc(ev)) == '*' ) // nested comment?
+          ++depth; // depth of comments has increased by one
+        else if ( c != EOF ) // need to put the char back?
+          s->Ungetc(c); // especially need to put back '/', 0xA, or 0xD
+      }
+        
+      if ( ev->Bad() )
+        c = EOF;
+    }
+    if ( c == EOF && depth > 0 )
+      ev->NewWarning("EOF before end of comment");
+  }
+  else
+    ev->NewWarning("expected / or *");
+  
+  return c;
+}
+
+int morkParser::eat_line_break(morkEnv* ev, int inLast)
+{
+  morkStream* s = mParser_Stream;
+  register int c = s->Getc(ev); // get next char after 0xA or 0xD
+  this->CountLineBreak();
+  if ( c == 0xA || c == 0xD ) // another line break character?
+  {
+    if ( c != inLast ) // not the same as the last one?
+      c = s->Getc(ev); // get next char after two-byte linebreak
+  }
+  return c;
+}
+
+int morkParser::eat_line_continue(morkEnv* ev) // last char was '\'
+{
+  morkStream* s = mParser_Stream;
+  register int c = s->Getc(ev);
+  if ( c == 0xA || c == 0xD ) // linebreak follows \ as expected?
+  {
+    c = this->eat_line_break(ev, c);
+  }
+  else
+    ev->NewWarning("expected linebreak");
+  
+  return c;
+}
+
+int morkParser::NextChar(morkEnv* ev) // next non-white content
+{
+  morkStream* s = mParser_Stream;
+  register int c = s->Getc(ev);
+  while ( c > 0 && ev->Good() )
+  {
+    if ( c == '/' )
+      c = this->eat_comment(ev);
+    else if ( c == 0xA || c == 0xD )
+      c = this->eat_line_break(ev, c);
+    else if ( c == '\\' )
+      c = this->eat_line_continue(ev);
+    else if ( morkCh_IsWhite(c) )
+      c = s->Getc(ev);
+    else  
+      break; // end while loop when return c is acceptable
+  }
+  if ( ev->Bad() )
+  {
+    mParser_State = morkParser_kBrokenState;
+    mParser_DoMore = morkBool_kFalse;
+    mParser_IsDone = morkBool_kTrue;
+    mParser_IsBroken = morkBool_kTrue;
+    c = EOF;
+  }
+  else if ( c == EOF )
+  {
+    mParser_DoMore = morkBool_kFalse;
+    mParser_IsDone = morkBool_kTrue;
+  }
+  return c;
+}
+
+void
+morkParser::OnCellState(morkEnv* ev)
+{
+  ev->StubMethodOnlyError();
+}
+
+void
+morkParser::OnMetaState(morkEnv* ev)
+{
+  ev->StubMethodOnlyError();
+}
+
+void
+morkParser::OnRowState(morkEnv* ev)
+{
+  ev->StubMethodOnlyError();
+}
+
+void
+morkParser::OnTableState(morkEnv* ev)
+{
+  ev->StubMethodOnlyError();
+}
+
+void
+morkParser::OnDictState(morkEnv* ev)
+{
+  ev->StubMethodOnlyError();
+}
+
+morkBuf* morkParser::ReadName(morkEnv* ev, register int c)
+{
+  morkBuf* outBuf = 0;
+  
+  if ( !morkCh_IsName(c) )
+    ev->NewError("not a name char");
+
+  morkCoil* coil = &mParser_ColumnCoil;
+  coil->ClearBufFill();
+
+  morkSpool* spool = &mParser_ColumnSpool;
+  spool->Seek(ev, /*pos*/ 0);
+  
+  if ( ev->Good() )
+  {
+    spool->Putc(ev, c);
+    
+    morkStream* s = mParser_Stream;
+    while ( (c = s->Getc(ev)) != EOF && morkCh_IsMore(c) && ev->Good() )
+      spool->Putc(ev, c);
+      
+    if ( ev->Good() )
+    {
+      if ( c != EOF )
+      {
+        s->Ungetc(c);
+        spool->FlushSink(ev); // update coil->mBuf_Fill
+      }
+      else
+        this->UnexpectedEofError(ev);
+        
+      if ( ev->Good() )
+        outBuf = coil;
+    }
+  }  
+  return outBuf;
+}
+
+mork_bool
+morkParser::ReadMid(morkEnv* ev, morkMid* outMid)
+{
+  outMid->ClearMid();
+  
+  morkStream* s = mParser_Stream;
+  int next;
+  outMid->mMid_Oid.mOid_Id = this->ReadHex(ev, &next);
+  register int c = next;
+  if ( c == ':' )
+  {
+    if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+    {
+      if ( c == '^' )
+      {
+        outMid->mMid_Oid.mOid_Scope = this->ReadHex(ev, &next);
+        if ( ev->Good() )
+          s->Ungetc(next);
+      }
+      else if ( morkCh_IsName(c) )
+      {
+        outMid->mMid_Buf = this->ReadName(ev, c); 
+      }
+      else
+        ev->NewError("expected name or hex after ':' following ID");
+    }
+    
+    if ( c == EOF && ev->Good() )
+      this->UnexpectedEofError(ev);
+  }
+  else
+    s->Ungetc(c);
+  
+  return ev->Good();
+}
+
+void
+morkParser::ReadCell(morkEnv* ev)
+{
+  mParser_CellMid.ClearMid();
+  // this->StartSpanOnLastByte(ev, &mParser_CellSpan);
+  morkMid* cellMid = 0; // if mid syntax is used for column
+  morkBuf* cellBuf = 0; // if naked string is used for column
+
+  morkStream* s = mParser_Stream;
+  register int c;
+  if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+  {
+    // this->StartSpanOnLastByte(ev, &mParser_ColumnSpan);
+    if ( c == '^' )
+    {
+      cellMid = &mParser_CellMid;
+      this->ReadMid(ev, cellMid);
+      // if ( !mParser_CellMid.mMid_Oid.mOid_Scope )
+      //  mParser_CellMid.mMid_Oid.mOid_Scope = (mork_scope) 'c';
+    }
+    else
+    {
+      if (mParser_InMeta && c == morkStore_kFormColumn)
+      {
+        ReadCellForm(ev, c);
+        return;
+      }
+      else
+        cellBuf = this->ReadName(ev, c); 
+    }
+    if ( ev->Good() )
+    {
+      // this->EndSpanOnThisByte(ev, &mParser_ColumnSpan);
+
+      mParser_InCell = morkBool_kTrue;
+      this->OnNewCell(ev, *mParser_CellSpan.AsPlace(),
+        cellMid, cellBuf); // , mParser_CellChange
+
+      mParser_CellChange = morkChange_kNil;
+      if ( (c = this->NextChar(ev)) != EOF && ev->Good() )
+      {
+        // this->StartSpanOnLastByte(ev, &mParser_SlotSpan);
+        if ( c == '=' )
+        {
+          morkBuf* buf = this->ReadValue(ev);
+          if ( buf )
+          {
+            // this->EndSpanOnThisByte(ev, &mParser_SlotSpan);
+            this->OnValue(ev, mParser_SlotSpan, *buf);
+          }
+        }
+        else if ( c == '^' )
+        {
+          if ( this->ReadMid(ev, &mParser_Mid) )
+          {
+            // this->EndSpanOnThisByte(ev, &mParser_SlotSpan);
+            if ( (c = this->NextChar(ev)) != EOF && ev->Good() )
+            {
+              if ( c != ')' )
+                ev->NewError("expected ')' after cell ^ID value");
+            }
+            else if ( c == EOF )
+              this->UnexpectedEofError(ev);
+            
+            if ( ev->Good() )
+              this->OnValueMid(ev, mParser_SlotSpan, mParser_Mid);
+          }
+        }
+        else if ( c == 'r' || c == 't' || c == '"' || c == '\'' )
+        {
+          ev->NewError("cell syntax not yet supported");
+        }
+        else
+        {
+          ev->NewError("unknown cell syntax");
+        }
+      }
+      
+      // this->EndSpanOnThisByte(ev, &mParser_CellSpan);
+      mParser_InCell = morkBool_kFalse;
+      this->OnCellEnd(ev, mParser_CellSpan);
+    }
+  }
+  mParser_CellChange = morkChange_kNil;
+  
+  if ( c == EOF && ev->Good() )
+    this->UnexpectedEofError(ev);
+}
+
+void morkParser::ReadRowPos(morkEnv* ev)
+{
+  int c; // next character
+  mork_pos rowPos = this->ReadHex(ev, &c);
+  
+  if ( ev->Good() && c != EOF ) // should put back byte after hex?
+    mParser_Stream->Ungetc(c);
+
+  this->OnRowPos(ev, rowPos);
+}
+
+void morkParser::ReadRow(morkEnv* ev, int c)
+// zm:Row       ::= zm:S? '[' zm:S? zm:Id zm:RowItem* zm:S? ']'
+// zm:RowItem   ::= zm:MetaRow | zm:Cell
+// zm:MetaRow   ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */
+// zm:Cell      ::= zm:S? '(' zm:Column zm:S? zm:Slot? ')'
+{
+  if ( ev->Good() )
+  {
+    // this->StartSpanOnLastByte(ev, &mParser_RowSpan);
+    if ( mParser_Change )
+      mParser_RowChange = mParser_Change;
+
+    mork_bool cutAllRowCols = morkBool_kFalse;
+
+    if ( c == '[' )
+    {
+      if ( ( c = this->NextChar(ev) ) == '-' )
+        cutAllRowCols = morkBool_kTrue;
+      else if ( ev->Good() && c != EOF )
+        mParser_Stream->Ungetc(c);
+
+      if ( this->ReadMid(ev, &mParser_RowMid) )
+      {
+        mParser_InRow = morkBool_kTrue;
+        this->OnNewRow(ev, *mParser_RowSpan.AsPlace(),
+          mParser_RowMid, cutAllRowCols);
+
+        mParser_Change = mParser_RowChange = morkChange_kNil;
+
+        while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != ']' )
+        {
+          switch ( c )
+          {
+            case '(': // cell
+              this->ReadCell(ev);
+              break;
+              
+            case '[': // meta
+              this->ReadMeta(ev, ']');
+              break;
+            
+            // case '+': // plus
+            //   mParser_CellChange = morkChange_kAdd;
+            //   break;
+              
+            case '-': // minus
+              // mParser_CellChange = morkChange_kCut;
+              this->OnMinusCell(ev);
+              break;
+              
+            // case '!': // bang
+            //   mParser_CellChange = morkChange_kSet;
+            //  break;
+              
+            default:
+              ev->NewWarning("unexpected byte in row");
+              break;
+          } // switch
+        } // while
+        
+        if ( ev->Good() )
+        {
+          if ( (c = this->NextChar(ev)) == '!' )
+            this->ReadRowPos(ev);
+          else if ( c != EOF && ev->Good() )
+            mParser_Stream->Ungetc(c);
+        }
+        
+        // this->EndSpanOnThisByte(ev, &mParser_RowSpan);
+        mParser_InRow = morkBool_kFalse;
+        this->OnRowEnd(ev, mParser_RowSpan);
+
+      } // if ReadMid
+    } // if '['
+    
+    else // c != '['
+    {
+      morkStream* s = mParser_Stream;
+      s->Ungetc(c);
+      if ( this->ReadMid(ev, &mParser_RowMid) )
+      {
+        mParser_InRow = morkBool_kTrue;
+        this->OnNewRow(ev, *mParser_RowSpan.AsPlace(),
+          mParser_RowMid, cutAllRowCols);
+
+        mParser_Change = mParser_RowChange = morkChange_kNil;
+        
+        if ( ev->Good() )
+        {
+          if ( (c = this->NextChar(ev)) == '!' )
+            this->ReadRowPos(ev);
+          else if ( c != EOF && ev->Good() )
+            s->Ungetc(c);
+        }
+
+        // this->EndSpanOnThisByte(ev, &mParser_RowSpan);
+        mParser_InRow = morkBool_kFalse;
+        this->OnRowEnd(ev, mParser_RowSpan);
+      }
+    }
+  }
+  
+  if ( ev->Bad() )
+    mParser_State = morkParser_kBrokenState;
+  else if ( c == EOF )
+    mParser_State = morkParser_kDoneState;
+}
+
+void morkParser::ReadTable(morkEnv* ev)
+// zm:Table     ::= zm:S? '{' zm:S? zm:Id zm:TableItem* zm:S? '}'
+// zm:TableItem ::= zm:MetaTable | zm:RowRef | zm:Row
+// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */
+{
+  // this->StartSpanOnLastByte(ev, &mParser_TableSpan);
+
+  if ( mParser_Change )
+    mParser_TableChange = mParser_Change;
+
+  mork_bool cutAllTableRows = morkBool_kFalse;
+  
+  int c = this->NextChar(ev);
+  if ( c == '-' )
+    cutAllTableRows = morkBool_kTrue;
+  else if ( ev->Good() && c != EOF )
+    mParser_Stream->Ungetc(c);
+  
+  if ( ev->Good() && this->ReadMid(ev, &mParser_TableMid) )
+  {
+    mParser_InTable = morkBool_kTrue;
+    this->OnNewTable(ev, *mParser_TableSpan.AsPlace(),  
+      mParser_TableMid, cutAllTableRows);
+
+    mParser_Change = mParser_TableChange = morkChange_kNil;
+
+    while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != '}' )
+    {
+      if ( morkCh_IsHex(c) )
+      {
+        this->ReadRow(ev, c);
+      }
+      else
+      {
+        switch ( c )
+        {
+          case '[': // row
+            this->ReadRow(ev, '[');
+            break;
+            
+          case '{': // meta
+            this->ReadMeta(ev, '}');
+            break;
+          
+          // case '+': // plus
+          //   mParser_RowChange = morkChange_kAdd;
+          //   break;
+            
+          case '-': // minus
+            // mParser_RowChange = morkChange_kCut;
+            this->OnMinusRow(ev);
+            break;
+            
+          // case '!': // bang
+          //   mParser_RowChange = morkChange_kSet;
+          //   break;
+            
+          default:
+            ev->NewWarning("unexpected byte in table");
+            break;
+        }
+      }
+    }
+
+    // this->EndSpanOnThisByte(ev, &mParser_TableSpan);
+    mParser_InTable = morkBool_kFalse;
+    this->OnTableEnd(ev, mParser_TableSpan);
+
+    if ( ev->Bad() )
+      mParser_State = morkParser_kBrokenState;
+    else if ( c == EOF )
+      mParser_State = morkParser_kDoneState;
+  }
+}
+
+mork_id morkParser::ReadHex(morkEnv* ev, int* outNextChar)
+// zm:Hex   ::= [0-9a-fA-F] /* a single hex digit */
+// zm:Hex+  ::= zm:Hex | zm:Hex zm:Hex+
+{
+  mork_id hex = 0;
+
+  morkStream* s = mParser_Stream;
+  register int c = this->NextChar(ev);
+    
+  if ( ev->Good() )
+  {
+    if ( c != EOF )
+    {
+      if ( morkCh_IsHex(c) )
+      {
+        do
+        {
+          if ( morkCh_IsDigit(c) ) // '0' through '9'?
+            c -= '0';
+          else if ( morkCh_IsUpper(c) ) // 'A' through 'F'?
+            c -= ('A' - 10) ; // c = (c - 'A') + 10;
+          else // 'a' through 'f'?
+            c -= ('a' - 10) ; // c = (c - 'a') + 10;
+
+          hex = (hex << 4) + c;
+        }
+        while ( (c = s->Getc(ev)) != EOF && ev->Good() && morkCh_IsHex(c) );
+      }
+      else
+        this->ExpectedHexDigitError(ev, c);
+    }
+  }
+  if ( c == EOF )
+    this->EofInsteadOfHexError(ev);
+    
+  *outNextChar = c;
+  return hex;
+}
+
+/*static*/ void
+morkParser::EofInsteadOfHexError(morkEnv* ev)
+{
+  ev->NewWarning("eof instead of hex");
+}
+
+/*static*/ void
+morkParser::ExpectedHexDigitError(morkEnv* ev, int c)
+{
+  MORK_USED_1(c);
+  ev->NewWarning("expected hex digit");
+}
+
+/*static*/ void
+morkParser::ExpectedEqualError(morkEnv* ev)
+{
+  ev->NewWarning("expected '='");
+}
+
+/*static*/ void
+morkParser::UnexpectedEofError(morkEnv* ev)
+{
+  ev->NewWarning("unexpected eof");
+}
+
+
+morkBuf* morkParser::ReadValue(morkEnv* ev)
+{
+  morkBuf* outBuf = 0;
+
+  morkCoil* coil = &mParser_ValueCoil;
+  coil->ClearBufFill();
+
+  morkSpool* spool = &mParser_ValueSpool;
+  spool->Seek(ev, /*pos*/ 0);
+  
+  if ( ev->Good() )
+  {
+    morkStream* s = mParser_Stream;
+    register int c;
+    while ( (c = s->Getc(ev)) != EOF && c != ')' && ev->Good() )
+    {
+      if ( c == '\\' ) // next char is escaped by '\'? 
+      {
+        if ( (c = s->Getc(ev)) == 0xA || c == 0xD ) // linebreak after \?
+        {
+          c = this->eat_line_break(ev, c);
+          if ( c == ')' || c == '\\' || c == '$' )
+          {
+            s->Ungetc(c); // just let while loop test read this again
+            continue; // goto next iteration of while loop
+          }
+        }
+        if ( c == EOF || ev->Bad() )
+          break; // end while loop
+      }
+      else if ( c == '$' ) // "$" escapes next two hex digits?
+      {
+        if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+        {
+          mork_ch first = (mork_ch) c; // first hex digit
+          if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+          {
+            mork_ch second = (mork_ch) c; // second hex digit
+            c = ev->HexToByte(first, second);
+          }
+          else
+            break; // end while loop
+        }
+        else
+          break; // end while loop
+      }
+      spool->Putc(ev, c);
+    }
+      
+    if ( ev->Good() )
+    {
+      if ( c != EOF )
+        spool->FlushSink(ev); // update coil->mBuf_Fill
+      else
+        this->UnexpectedEofError(ev);
+        
+      if ( ev->Good() )
+        outBuf = coil;
+    }
+  }
+  return outBuf; 
+}
+
+void morkParser::ReadDictForm(morkEnv *ev)
+{
+  int nextChar;
+  nextChar = this->NextChar(ev);
+  if (nextChar == '(')
+  {
+    nextChar = this->NextChar(ev);
+    if (nextChar == morkStore_kFormColumn)
+    {
+      int dictForm;
+
+      nextChar = this->NextChar(ev);
+      if (nextChar == '=')
+      {
+        dictForm = this->NextChar(ev);
+        nextChar = this->NextChar(ev);
+      }
+      else if (nextChar == '^')
+      {
+        dictForm = this->ReadHex(ev, &nextChar);
+      }
+      else
+      {
+        ev->NewWarning("unexpected byte in dict form");
+        return;
+      }
+      mParser_ValueCoil.mText_Form = dictForm;
+      if (nextChar == ')')
+      {
+        nextChar = this->NextChar(ev);
+        if (nextChar == '>')
+          return;
+      }
+    }
+  }
+  ev->NewWarning("unexpected byte in dict form");
+}
+
+void morkParser::ReadCellForm(morkEnv *ev, int c)
+{
+  MORK_ASSERT (c == morkStore_kFormColumn);
+  int nextChar;
+  nextChar = this->NextChar(ev);
+  int cellForm;
+
+  if (nextChar == '=')
+  {
+    cellForm = this->NextChar(ev);
+    nextChar = this->NextChar(ev);
+  }
+  else if (nextChar == '^')
+  {
+    cellForm = this->ReadHex(ev, &nextChar);
+  }
+  else
+  {
+    ev->NewWarning("unexpected byte in cell form");
+    return;
+  }
+  // ### not sure about this. Which form should we set?
+  //    mBuilder_CellForm = mBuilder_RowForm = cellForm;
+  if (nextChar == ')')
+  {
+    OnCellForm(ev, cellForm);
+    return;
+  }
+  ev->NewWarning("unexpected byte in cell form");
+}
+
+void morkParser::ReadAlias(morkEnv* ev)
+// zm:Alias     ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')'
+// zm:Value   ::= '=' ([^)$\] | '\' zm:NonCRLF | zm:Continue | zm:Dollar)*
+{
+  // this->StartSpanOnLastByte(ev, &mParser_AliasSpan);
+
+  int nextChar;
+  mork_id hex = this->ReadHex(ev, &nextChar);
+  register int c = nextChar;
+
+  mParser_Mid.ClearMid();
+  mParser_Mid.mMid_Oid.mOid_Id = hex;
+
+  if ( morkCh_IsWhite(c) && ev->Good() )
+    c = this->NextChar(ev);
+
+  if ( ev->Good() )
+  {
+    if ( c == '<')
+    {
+      ReadDictForm(ev);
+      if (ev->Good())
+        c = this->NextChar(ev);
+    }
+    if ( c == '=' )
+    {
+      mParser_Mid.mMid_Buf = this->ReadValue(ev);
+      if ( mParser_Mid.mMid_Buf )
+      {
+        // this->EndSpanOnThisByte(ev, &mParser_AliasSpan);
+        this->OnAlias(ev, mParser_AliasSpan, mParser_Mid);
+        // need to reset this somewhere.
+        mParser_ValueCoil.mText_Form = 0;
+
+      }
+    }
+    else
+      this->ExpectedEqualError(ev);
+  }
+}
+
+void morkParser::ReadMeta(morkEnv* ev, int inEndMeta)
+// zm:MetaDict  ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */
+// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */
+// zm:MetaRow   ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */
+{
+  // this->StartSpanOnLastByte(ev, &mParser_MetaSpan);
+  mParser_InMeta = morkBool_kTrue;
+  this->OnNewMeta(ev, *mParser_MetaSpan.AsPlace());
+
+  mork_bool more = morkBool_kTrue; // until end meta
+  int c;
+  while ( more && (c = this->NextChar(ev)) != EOF && ev->Good() )
+  {
+    switch ( c )
+    {
+      case '(': // cell
+        this->ReadCell(ev);
+        break;
+        
+      case '>': // maybe end meta?
+        if ( inEndMeta == '>' )
+          more = morkBool_kFalse; // stop reading meta
+        else
+          this->UnexpectedByteInMetaWarning(ev);
+        break;
+        
+      case '}': // maybe end meta?
+        if ( inEndMeta == '}' )
+          more = morkBool_kFalse; // stop reading meta
+        else
+          this->UnexpectedByteInMetaWarning(ev);
+        break;
+        
+      case ']': // maybe end meta?
+        if ( inEndMeta == ']' )
+          more = morkBool_kFalse; // stop reading meta
+        else
+          this->UnexpectedByteInMetaWarning(ev);
+        break;
+        
+      case '[': // maybe table meta row?
+        if ( mParser_InTable )
+          this->ReadRow(ev, '['); 
+        else
+          this->UnexpectedByteInMetaWarning(ev);
+        break;
+        
+      default:
+        if ( mParser_InTable && morkCh_IsHex(c) )
+          this->ReadRow(ev, c);
+        else
+          this->UnexpectedByteInMetaWarning(ev);
+        break;
+    }
+  }
+
+  // this->EndSpanOnThisByte(ev, &mParser_MetaSpan);
+  mParser_InMeta = morkBool_kFalse;
+  this->OnMetaEnd(ev, mParser_MetaSpan);
+}
+
+/*static*/ void
+morkParser::UnexpectedByteInMetaWarning(morkEnv* ev)
+{
+  ev->NewWarning("unexpected byte in meta");
+}
+
+/*static*/ void
+morkParser::NonParserTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkParser");
+}
+
+mork_bool morkParser::MatchPattern(morkEnv* ev, const char* inPattern)
+{
+  // if an error occurs, we want original inPattern in the debugger:
+  const char* pattern = inPattern; // mutable copy of pointer
+  morkStream* s = mParser_Stream;
+  register int c;
+  while ( *pattern && ev->Good() )
+  {
+    char byte = *pattern++;
+    if ( (c = s->Getc(ev)) != byte )
+    {
+      ev->NewError("byte not in expected pattern");
+    }
+  }
+  return ev->Good();
+}
+
+mork_bool morkParser::FindGroupEnd(morkEnv* ev)
+{
+  mork_bool foundEnd = morkBool_kFalse;
+  
+  // char gidBuf[ 64 ]; // to hold hex pattern we want
+  // (void) ev->TokenAsHex(gidBuf, mParser_GroupId);
+  
+  morkStream* s = mParser_Stream;
+  register int c;
+  
+  while ( (c = s->Getc(ev)) != EOF && ev->Good() && !foundEnd )
+  {
+    if ( c == '@' ) // maybe start of group ending?
+    {
+      // this->EndSpanOnThisByte(ev, &mParser_GroupSpan);
+      if ( (c = s->Getc(ev)) == '$' ) // '$' follows '@' ?
+      {
+        if ( (c = s->Getc(ev)) == '$' ) // '$' follows "@$" ?
+        {
+          if ( (c = s->Getc(ev)) == '}' )
+          {
+            foundEnd = this->ReadEndGroupId(ev);
+            // this->EndSpanOnThisByte(ev, &mParser_GroupSpan);
+
+          }
+          else
+            ev->NewError("expected '}' after @$$");
+        }
+      }
+      if ( !foundEnd && c == '@' )
+        s->Ungetc(c);
+    }
+  }
+
+  return foundEnd && ev->Good();
+}
+
+void morkParser::ReadGroup(morkEnv* mev)
+{
+  nsIMdbEnv *ev = mev->AsMdbEnv();
+  int next = 0;
+  mParser_GroupId = this->ReadHex(mev, &next);
+  if ( next == '{' )
+  {
+    morkStream* s = mParser_Stream;
+     register int c;
+    if ( (c = s->Getc(mev)) == '@' )
+    {
+    	// we really need the following span inside morkBuilder::OnNewGroup():
+      this->StartSpanOnThisByte(mev, &mParser_GroupSpan);
+      mork_pos startPos = mParser_GroupSpan.mSpan_Start.mPlace_Pos;
+
+      // if ( !store->mStore_FirstCommitGroupPos )
+      //   store->mStore_FirstCommitGroupPos = startPos;
+      // else if ( !store->mStore_SecondCommitGroupPos )
+      //   store->mStore_SecondCommitGroupPos = startPos;
+      
+      if ( this->FindGroupEnd(mev) )
+      {
+        mork_pos outPos;
+        s->Seek(ev, startPos, &outPos);
+        if ( mev->Good() )
+        {
+          this->OnNewGroup(mev, mParser_GroupSpan.mSpan_Start,
+            mParser_GroupId);
+          
+          this->ReadContent(mev, /*inInsideGroup*/ morkBool_kTrue);
+
+          this->OnGroupCommitEnd(mev, mParser_GroupSpan);
+        }
+      }
+    }
+    else
+      mev->NewError("expected '@' after @$${id{");
+  }
+  else
+    mev->NewError("expected '{' after @$$id");
+    
+}
+
+mork_bool morkParser::ReadAt(morkEnv* ev, mork_bool inInsideGroup)
+/* groups must be ignored until properly terminated */
+// zm:Group       ::= zm:GroupStart zm:Content zm:GroupEnd /* transaction */
+// zm:GroupStart  ::= zm:S? '@$${' zm:Hex+ '{@' /* xaction id has own space */
+// zm:GroupEnd    ::= zm:GroupCommit | zm:GroupAbort
+// zm:GroupCommit ::= zm:S? '@$$}' zm:Hex+ '}@'  /* id matches start id */
+// zm:GroupAbort  ::= zm:S? '@$$}~~}@' /* id matches start id */
+/* We must allow started transactions to be aborted in summary files. */
+/* Note '$$' will never occur unescaped in values we will see in Mork. */
+{
+  if ( this->MatchPattern(ev, "$$") )
+  {
+    morkStream* s = mParser_Stream;
+     register int c;
+    if ( ((c = s->Getc(ev)) == '{' || c == '}') && ev->Good() )
+     {
+       if ( c == '{' ) // start of new group?
+       {
+         if ( !inInsideGroup )
+           this->ReadGroup(ev);
+         else
+           ev->NewError("nested @$${ inside another group");
+       }
+       else // c == '}' // end of old group?
+       {
+         if ( inInsideGroup )
+         {
+          this->ReadEndGroupId(ev);
+          mParser_GroupId = 0;
+         }
+         else
+           ev->NewError("unmatched @$$} outside any group");
+       }
+     }
+     else
+       ev->NewError("expected '{' or '}' after @$$");
+  }
+  return ev->Good();
+}
+
+mork_bool morkParser::ReadEndGroupId(morkEnv* ev)
+{
+  mork_bool outSawGroupId = morkBool_kFalse;
+  morkStream* s = mParser_Stream;
+  register int c;
+  if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+  {
+    if ( c == '~' ) // transaction is aborted?
+    {
+      this->MatchPattern(ev, "~}@"); // finish rest of pattern
+    }
+    else // push back byte and read expected trailing hex id
+    {
+      s->Ungetc(c);
+      int next = 0;
+      mork_gid endGroupId = this->ReadHex(ev, &next);
+      if ( ev->Good() )
+      {
+        if ( endGroupId == mParser_GroupId ) // matches start?
+        {
+          if ( next == '}' ) // '}' after @$$}id ?
+          {
+            if ( (c = s->Getc(ev)) == '@' ) // '@' after @$$}id} ?
+            {
+              // looks good, so return with no error
+              outSawGroupId = morkBool_kTrue;
+              mParser_InGroup = false;
+            }
+            else
+              ev->NewError("expected '@' after @$$}id}");
+          }
+          else
+            ev->NewError("expected '}' after @$$}id");
+        }
+        else
+          ev->NewError("end group id mismatch");
+      }
+    }
+  }
+  return ( outSawGroupId && ev->Good() );
+}
+
+
+void morkParser::ReadDict(morkEnv* ev)
+// zm:Dict      ::= zm:S? '<' zm:DictItem* zm:S? '>'
+// zm:DictItem  ::= zm:MetaDict | zm:Alias
+// zm:MetaDict  ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */
+// zm:Alias     ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')'
+{
+  mParser_Change = morkChange_kNil;
+  mParser_AtomChange = morkChange_kNil;
+  
+  // this->StartSpanOnLastByte(ev, &mParser_DictSpan);
+  mParser_InDict = morkBool_kTrue;
+  this->OnNewDict(ev, *mParser_DictSpan.AsPlace());
+  
+  int c;
+  while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != '>' )
+  {
+    switch ( c )
+    {
+      case '(': // alias
+        this->ReadAlias(ev);
+        break;
+        
+      case '<': // meta
+        this->ReadMeta(ev, '>');
+        break;
+        
+      default:
+        ev->NewWarning("unexpected byte in dict");
+        break;
+    }
+  }
+
+  // this->EndSpanOnThisByte(ev, &mParser_DictSpan);
+  mParser_InDict = morkBool_kFalse;
+  this->OnDictEnd(ev, mParser_DictSpan);
+  
+  if ( ev->Bad() )
+    mParser_State = morkParser_kBrokenState;
+  else if ( c == EOF )
+    mParser_State = morkParser_kDoneState;
+}
+
+void morkParser::EndSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan)
+{
+  mork_pos here;
+  nsIMdbEnv *ev = mev->AsMdbEnv();
+  nsresult rv = mParser_Stream->Tell(ev, &here);
+  if (NS_SUCCEEDED(rv) && mev->Good() )
+  {
+    this->SetHerePos(here);
+    ioSpan->SetEndWithEnd(mParser_PortSpan);
+  }
+}
+
+void morkParser::EndSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan)
+{
+  mork_pos here;
+  nsIMdbEnv *ev = mev->AsMdbEnv();
+  nsresult rv= mParser_Stream->Tell(ev, &here);
+  if ( NS_SUCCEEDED(rv) && mev->Good() )
+  {
+    if ( here > 0 )
+      --here;
+    else
+      here = 0;
+
+    this->SetHerePos(here);
+    ioSpan->SetEndWithEnd(mParser_PortSpan);
+  }
+}
+
+void morkParser::StartSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan)
+{
+  mork_pos here;
+  nsIMdbEnv *ev = mev->AsMdbEnv();
+  nsresult rv = mParser_Stream->Tell(ev, &here);
+  if ( NS_SUCCEEDED(rv) && mev->Good() )
+  {
+    if ( here > 0 )
+      --here;
+    else
+      here = 0;
+
+    this->SetHerePos(here);
+    ioSpan->SetStartWithEnd(mParser_PortSpan);
+    ioSpan->SetEndWithEnd(mParser_PortSpan);
+  }
+}
+
+void morkParser::StartSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan)
+{
+  mork_pos here;
+  nsIMdbEnv *ev = mev->AsMdbEnv();
+  nsresult rv = mParser_Stream->Tell(ev, &here);
+  if ( NS_SUCCEEDED(rv) && mev->Good() )
+  {
+    this->SetHerePos(here);
+    ioSpan->SetStartWithEnd(mParser_PortSpan);
+    ioSpan->SetEndWithEnd(mParser_PortSpan);
+  }
+}
+
+mork_bool
+morkParser::ReadContent(morkEnv* ev, mork_bool inInsideGroup)
+{
+  int c;
+  mork_bool keep_going = true;
+  while ( keep_going && (c = this->NextChar(ev)) != EOF && ev->Good())
+  {
+    switch ( c )
+    {
+      case '[': // row
+        this->ReadRow(ev, '[');
+        keep_going = false;
+        break;
+        
+      case '{': // table
+        this->ReadTable(ev);
+        keep_going = false;
+        break;
+        
+      case '<': // dict
+        this->ReadDict(ev);
+        keep_going = false;
+        break;
+        
+      case '@': // group
+        return this->ReadAt(ev, inInsideGroup);
+        // break;
+        
+      // case '+': // plus
+      //   mParser_Change = morkChange_kAdd;
+      //   break;
+        
+      // case '-': // minus
+      //   mParser_Change = morkChange_kCut;
+      //   break;
+        
+      // case '!': // bang
+      //   mParser_Change = morkChange_kSet;
+      //   break;
+        
+      default:
+        ev->NewWarning("unexpected byte in ReadContent()");
+        break;
+    }
+  }
+  if ( ev->Bad() )
+    mParser_State = morkParser_kBrokenState;
+  else if ( c == EOF )
+    mParser_State = morkParser_kDoneState;
+    
+  return ( ev->Good() && c != EOF );
+}
+
+void
+morkParser::OnPortState(morkEnv* ev)
+{
+  mork_bool firstTime = !mParser_InPort;
+  mParser_InPort = morkBool_kTrue;
+  if (firstTime)
+    this->OnNewPort(ev, *mParser_PortSpan.AsPlace());
+
+  mork_bool done = !this->ReadContent(ev, mParser_InGroup/*inInsideGroup*/);
+
+  if (done)
+  {
+    mParser_InPort = morkBool_kFalse;
+    this->OnPortEnd(ev, mParser_PortSpan);
+  }
+  
+  if ( ev->Bad() )
+    mParser_State = morkParser_kBrokenState;
+}
+
+void
+morkParser::OnStartState(morkEnv* mev)
+{
+  morkStream* s = mParser_Stream;
+  nsIMdbEnv *ev = mev->AsMdbEnv();
+  if ( s && s->IsNode() && s->IsOpenNode() )
+  {
+    mork_pos outPos;
+    nsresult rv = s->Seek(ev, 0, &outPos);
+    if (NS_SUCCEEDED(rv) && mev->Good() )
+    {
+      this->StartParse(mev);
+      mParser_State = morkParser_kPortState;
+    }
+  }
+  else
+    mev->NilPointerError();
+
+  if ( mev->Bad() )
+    mParser_State = morkParser_kBrokenState;
+}
+
+/*protected non-poly*/ void
+morkParser::ParseChunk(morkEnv* ev)
+{
+  mParser_Change = morkChange_kNil;
+  mParser_DoMore = morkBool_kTrue;
+            
+  switch ( mParser_State )
+  {
+    case morkParser_kCellState: // 0
+      this->OnCellState(ev); break;
+      
+    case morkParser_kMetaState: // 1
+      this->OnMetaState(ev); break;
+      
+    case morkParser_kRowState: // 2
+      this->OnRowState(ev); break;
+      
+    case morkParser_kTableState: // 3
+      this->OnTableState(ev); break;
+      
+    case morkParser_kDictState: // 4
+      this->OnDictState(ev); break;
+      
+    case morkParser_kPortState: // 5
+      this->OnPortState(ev); break;
+      
+    case morkParser_kStartState: // 6
+      this->OnStartState(ev); break;
+     
+    case morkParser_kDoneState: // 7
+      mParser_DoMore = morkBool_kFalse;
+      mParser_IsDone = morkBool_kTrue;
+      this->StopParse(ev);
+      break;
+    case morkParser_kBrokenState: // 8
+      mParser_DoMore = morkBool_kFalse;
+      mParser_IsBroken = morkBool_kTrue;
+      this->StopParse(ev);
+      break;
+    default: // ?
+      MORK_ASSERT(morkBool_kFalse);
+      mParser_State = morkParser_kBrokenState;
+      break;
+  }
+}
+    
+/*public non-poly*/ mdb_count
+morkParser::ParseMore( // return count of bytes consumed now
+    morkEnv* ev,          // context
+    mork_pos* outPos,     // current byte pos in the stream afterwards
+    mork_bool* outDone,   // is parsing finished?
+    mork_bool* outBroken  // is parsing irreparably dead and broken?
+  )
+{
+  mdb_count outCount = 0;
+  if ( this->IsNode() && this->GoodParserTag() && this->IsOpenNode() )
+  {
+    mork_pos startPos = this->HerePos();
+
+    if ( !mParser_IsDone && !mParser_IsBroken )
+      this->ParseChunk(ev);
+  
+    // HerePos is only updated for groups. I'd like it to be more accurate.
+
+    mork_pos here;
+    mParser_Stream->Tell(ev, &here);
+
+    if ( outDone )
+      *outDone = mParser_IsDone;
+    if ( outBroken )
+      *outBroken = mParser_IsBroken;
+    if ( outPos )
+      *outPos = here;
+      
+    if ( here > startPos )
+      outCount = (mdb_count) (here - startPos);
+  }
+  else
+  {
+    this->NonUsableParserError(ev);
+    if ( outDone )
+      *outDone = morkBool_kTrue;
+    if ( outBroken )
+      *outBroken = morkBool_kTrue;
+    if ( outPos )
+      *outPos = 0;
+  }
+  return outCount;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkParser.h
@@ -0,0 +1,565 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKPARSER_
+#define _MORKPARSER_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKSINK_
+#include "morkSink.h"
+#endif
+
+#ifndef _MORKYARN_
+#include "morkYarn.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+ 
+/*=============================================================================
+ * morkPlace: stream byte position and stream line count
+ */
+
+class morkPlace {
+public:
+  mork_pos   mPlace_Pos;   // byte offset in an input stream
+  mork_line  mPlace_Line;  // line count in an input stream
+  
+  void ClearPlace()
+  {
+    mPlace_Pos = 0; mPlace_Line = 0;
+  }
+
+  void SetPlace(mork_pos inPos, mork_line inLine)
+  {
+    mPlace_Pos = inPos; mPlace_Line = inLine;
+  }
+
+  morkPlace() { mPlace_Pos = 0; mPlace_Line = 0; }
+  
+  morkPlace(mork_pos inPos, mork_line inLine)
+  { mPlace_Pos = inPos; mPlace_Line = inLine; }
+  
+  morkPlace(const morkPlace& inPlace)
+  : mPlace_Pos(inPlace.mPlace_Pos), mPlace_Line(inPlace.mPlace_Line) { }
+};
+
+/*=============================================================================
+ * morkGlitch: stream place and error comment describing a parsing error
+ */
+
+class morkGlitch {
+public:
+  morkPlace    mGlitch_Place;   // place in stream where problem happened
+  const char*  mGlitch_Comment; // null-terminated ASCII C string
+
+  morkGlitch() { mGlitch_Comment = 0; }
+  
+  morkGlitch(const morkPlace& inPlace, const char* inComment)
+  : mGlitch_Place(inPlace), mGlitch_Comment(inComment) { }
+};
+
+/*=============================================================================
+ * morkMid: all possible ways needed to express an alias ID in Mork syntax
+ */
+
+/*| morkMid: an abstraction of all the variations we might need to support
+**| in order to present an ID through the parser interface most cheaply and
+**| with minimum transformation away from the original text format.
+**|
+**|| An ID can have one of four forms:
+**| 1) idHex            (mMid_Oid.mOid_Id <- idHex)
+**| 2) idHex:^scopeHex  (mMid_Oid.mOid_Id <- idHex, mOid_Scope <- scopeHex)
+**| 3) idHex:scopeName  (mMid_Oid.mOid_Id <- idHex, mMid_Buf <- scopeName)
+**| 4) columnName       (mMid_Buf <- columnName, for columns in cells only)
+**|
+**|| Typically, mMid_Oid.mOid_Id will hold a nonzero integer value for
+**| an ID, but we might have an optional scope specified by either an integer
+**| in hex format, or a string name.  (Note that while the first ID can be
+**| scoped variably, any integer ID for a scope is assumed always located in
+**| the same scope, so the second ID need not be disambiguated.)
+**|
+**|| The only time mMid_Oid.mOid_Id is ever zero is when mMid_Buf alone
+**| is nonzero, to indicate an explicit string instead of an alias appeared.
+**| This case happens to make the representation of columns in cells somewhat
+**| easier to represent, since columns can just appear as a string name; and
+**| this unifies those interfaces with row and table APIs expecting IDs.
+**|
+**|| So when the parser passes an instance of morkMid to a subclass, the 
+**| mMid_Oid.mOid_Id slot should usually be nonzero.  And the other two
+**| slots, mMid_Oid.mOid_Scope and mMid_Buf, might both be zero, or at
+**| most one of them will be nonzero to indicate an explicit scope; the
+**| parser is responsible for ensuring at most one of these is nonzero.
+|*/
+class morkMid {
+public:
+  mdbOid          mMid_Oid;  // mOid_Scope is zero when not specified
+  const morkBuf*  mMid_Buf;  // points to some specific buf subclass
+  
+  morkMid()
+  { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
+   mMid_Buf = 0; }
+  
+  void InitMidWithCoil(morkCoil* ioCoil)
+  { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
+   mMid_Buf = ioCoil; }
+    
+  void ClearMid()
+  { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
+   mMid_Buf = 0; }
+
+  morkMid(const morkMid& other)
+  : mMid_Oid(other.mMid_Oid), mMid_Buf(other.mMid_Buf) { }
+  
+  mork_bool HasNoId() const // ID is unspecified?
+  { return ( mMid_Oid.mOid_Id == morkId_kMinusOne ); }
+  
+  mork_bool HasSomeId() const // ID is specified?
+  { return ( mMid_Oid.mOid_Id != morkId_kMinusOne ); }
+};
+
+/*=============================================================================
+ * morkSpan: start and end stream byte position and stream line count
+ */
+
+class morkSpan {
+public:
+  morkPlace   mSpan_Start;
+  morkPlace   mSpan_End;
+
+public: // methods
+  
+public: // inlines
+  morkSpan() { } // use inline empty constructor for each place
+  
+  morkPlace* AsPlace() { return &mSpan_Start; }
+  const morkPlace* AsConstPlace() const { return &mSpan_Start; }
+  
+  void SetSpan(mork_pos inFromPos, mork_line inFromLine,
+    mork_pos inToPos, mork_line inToLine)
+  {
+    mSpan_Start.SetPlace(inFromPos, inFromLine);
+    mSpan_End.SetPlace(inToPos,inToLine);
+  }
+
+  // setting end, useful to terminate a span using current port span end:
+  void SetEndWithEnd(const morkSpan& inSpan) // end <- span.end
+  { mSpan_End = inSpan.mSpan_End; }
+
+  // setting start, useful to initiate a span using current port span end:
+  void SetStartWithEnd(const morkSpan& inSpan) // start <- span.end
+  { mSpan_Start = inSpan.mSpan_End; }
+  
+  void ClearSpan()
+  {
+    mSpan_Start.mPlace_Pos = 0; mSpan_Start.mPlace_Line = 0;
+    mSpan_End.mPlace_Pos = 0; mSpan_End.mPlace_Line = 0;
+  }
+
+  morkSpan(mork_pos inFromPos, mork_line inFromLine,
+    mork_pos inToPos, mork_line inToLine)
+  : mSpan_Start(inFromPos, inFromLine), mSpan_End(inToPos, inToLine)
+  {  /* empty implementation */ }
+};
+
+/*=============================================================================
+ * morkParser: for parsing Mork text syntax
+ */
+
+#define morkParser_kMinGranularity 512 /* parse at least half 0.5K at once */
+#define morkParser_kMaxGranularity (64 * 1024) /* parse at most 64 K at once */
+
+#define morkDerived_kParser     /*i*/ 0x5073 /* ascii 'Ps' */
+#define morkParser_kTag     /*i*/ 0x70417253 /* ascii 'pArS' */
+
+// These are states for the simple parsing virtual machine.  Needless to say,
+// these must be distinct, and preferrably in a contiguous integer range.
+// Don't change these constants without looking at switch statements in code.
+#define morkParser_kCellState      0 /* cell is tightest scope */
+#define morkParser_kMetaState      1 /* meta is tightest scope */
+#define morkParser_kRowState       2 /* row is tightest scope */
+#define morkParser_kTableState     3 /* table is tightest scope */
+#define morkParser_kDictState      4 /* dict is tightest scope */
+#define morkParser_kPortState      5 /* port is tightest scope */
+
+#define morkParser_kStartState     6 /* parsing has not yet begun */
+#define morkParser_kDoneState      7 /* parsing is complete */
+#define morkParser_kBrokenState    8 /* parsing is to broken to work */
+
+class morkParser /*d*/ : public morkNode {
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+protected: // protected morkParser members
+  
+  nsIMdbHeap*   mParser_Heap;   // refcounted heap used for allocation
+  morkStream*   mParser_Stream; // refcounted input stream
+
+  mork_u4       mParser_Tag; // must equal morkParser_kTag
+  mork_count    mParser_MoreGranularity; // constructor inBytesPerParseSegment
+
+  mork_u4       mParser_State; // state where parser should resume
+
+  // after finding ends of group transactions, we can re-seek the start:
+  mork_pos      mParser_GroupContentStartPos; // start of this group
+
+  morkMid       mParser_TableMid; // table mid if inside a table
+  morkMid       mParser_RowMid;   // row mid if inside a row
+  morkMid       mParser_CellMid;  // cell mid if inside a row
+  mork_gid      mParser_GroupId;  // group ID if inside a group
+
+  mork_bool     mParser_InPort;  // called OnNewPort but not OnPortEnd?
+  mork_bool     mParser_InDict;  // called OnNewDict but not OnDictEnd?
+  mork_bool     mParser_InCell;  // called OnNewCell but not OnCellEnd?
+  mork_bool     mParser_InMeta;  // called OnNewMeta but not OnMetaEnd?
+
+  mork_bool     mParser_InPortRow;  // called OnNewPortRow but not OnPortRowEnd?
+  mork_bool     mParser_InRow;    // called OnNewRow but not OnNewRowEnd?
+  mork_bool     mParser_InTable;  // called OnNewMeta but not OnMetaEnd?
+  mork_bool     mParser_InGroup;  // called OnNewGroup but not OnGroupEnd?
+
+  mork_change   mParser_AtomChange;  // driven by mParser_Change 
+  mork_change   mParser_CellChange;  // driven by mParser_Change 
+  mork_change   mParser_RowChange;   // driven by mParser_Change 
+  mork_change   mParser_TableChange; // driven by mParser_Change 
+
+  mork_change   mParser_Change;     // driven by modifier in text 
+  mork_bool     mParser_IsBroken;   // has the parse become broken?
+  mork_bool     mParser_IsDone;     // has the parse finished?
+  mork_bool     mParser_DoMore;     // mParser_MoreGranularity not exhausted? 
+
+  morkMid       mParser_Mid;   // current alias being parsed
+  // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below:
+
+  // blob coils allocated in mParser_Heap
+  morkCoil     mParser_ScopeCoil;   // place to accumulate ID scope blobs
+  morkCoil     mParser_ValueCoil;   // place to accumulate value blobs
+  morkCoil     mParser_ColumnCoil;  // place to accumulate column blobs
+  morkCoil     mParser_StringCoil;  // place to accumulate string blobs
+
+  morkSpool    mParser_ScopeSpool;  // writes to mParser_ScopeCoil
+  morkSpool    mParser_ValueSpool;  // writes to mParser_ValueCoil
+  morkSpool    mParser_ColumnSpool; // writes to mParser_ColumnCoil
+  morkSpool    mParser_StringSpool; // writes to mParser_StringCoil
+
+  // yarns allocated in mParser_Heap
+  morkYarn      mParser_MidYarn;   // place to receive from MidToYarn()
+
+  // span showing current ongoing file position status:
+  morkSpan      mParser_PortSpan; // span of current db port file
+
+  // various spans denoting nested subspaces inside the file's port span:
+  morkSpan      mParser_GroupSpan; // span of current transaction group
+  morkSpan      mParser_DictSpan;
+  morkSpan      mParser_AliasSpan;
+  morkSpan      mParser_MetaSpan;
+  morkSpan      mParser_TableSpan;
+  morkSpan      mParser_RowSpan;
+  morkSpan      mParser_CellSpan;
+  morkSpan      mParser_ColumnSpan;
+  morkSpan      mParser_SlotSpan;
+
+private: // convenience inlines
+
+  mork_pos HerePos() const
+  { return mParser_PortSpan.mSpan_End.mPlace_Pos; }
+
+  void SetHerePos(mork_pos inPos)
+  { mParser_PortSpan.mSpan_End.mPlace_Pos = inPos; }
+
+  void CountLineBreak()
+  { ++mParser_PortSpan.mSpan_End.mPlace_Line; }
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseParser() only if open
+  virtual ~morkParser(); // assert that CloseParser() executed earlier
+  
+public: // morkYarn construction & destruction
+  morkParser(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+    morkStream* ioStream,  // the readonly stream for input bytes
+    mdb_count inBytesPerParseSegment, // target for ParseMore()
+    nsIMdbHeap* ioSlotHeap);
+      
+  void CloseParser(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkParser(const morkParser& other);
+  morkParser& operator=(const morkParser& other);
+
+public: // dynamic type identification
+  mork_bool IsParser() const
+  { return IsNode() && mNode_Derived == morkDerived_kParser; }
+  
+// } ===== end morkNode methods =====
+
+public: // errors and warnings
+  static void UnexpectedEofError(morkEnv* ev);
+  static void EofInsteadOfHexError(morkEnv* ev);
+  static void ExpectedEqualError(morkEnv* ev);
+  static void ExpectedHexDigitError(morkEnv* ev, int c);
+  static void NonParserTypeError(morkEnv* ev);
+  static void UnexpectedByteInMetaWarning(morkEnv* ev);
+
+public: // other type methods
+  mork_bool GoodParserTag() const { return mParser_Tag == morkParser_kTag; }
+  void NonGoodParserError(morkEnv* ev);
+  void NonUsableParserError(morkEnv* ev);
+  // call when IsNode() or GoodParserTag() is false
+  
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // in virtual morkParser methods, data flow subclass to parser
+
+    virtual void MidToYarn(morkEnv* ev,
+      const morkMid& inMid,  // typically an alias to concat with strings
+      mdbYarn* outYarn) = 0;
+    // The parser might ask that some aliases be turned into yarns, so they
+    // can be concatenated into longer blobs under some circumstances.  This
+    // is an alternative to using a long and complex callback for many parts
+    // for a single cell value.
+  
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // out virtual morkParser methods, data flow parser to subclass
+
+// The virtual methods below will be called in a pattern corresponding
+// to the following grammar isomorphic to the Mork grammar.  There should
+// be no exceptions, so subclasses can rely on seeing an appropriate "end"
+// method whenever some "new" method has been seen earlier.  In the event
+// that some error occurs that causes content to be flushed, or sudden early
+// termination of a larger containing entity, we will always call a more
+// enclosed "end" method before we call an "end" method with greater scope.
+
+// Note the "mp" prefix stands for "Mork Parser":
+
+// mp:Start     ::= OnNewPort mp:PortItem* OnPortEnd
+// mp:PortItem  ::= mp:Content | mp:Group | OnPortGlitch
+// mp:Group     ::= OnNewGroup mp:GroupItem* mp:GroupEnd
+// mp:GroupItem ::= mp:Content | OnGroupGlitch
+// mp:GroupEnd  ::= OnGroupCommitEnd | OnGroupAbortEnd
+// mp:Content   ::= mp:PortRow | mp:Dict | mp:Table | mp:Row
+// mp:PortRow   ::= OnNewPortRow mp:RowItem* OnPortRowEnd
+// mp:Dict      ::= OnNewDict mp:DictItem* OnDictEnd
+// mp:DictItem  ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch
+// mp:Table     ::= OnNewTable mp:TableItem* OnTableEnd
+// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch
+// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd
+// mp:Meta      ::= OnNewMeta mp:MetaItem* OnMetaEnd
+// mp:MetaItem  ::= mp:Cell | OnMetaGlitch
+// mp:Row       ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd
+// mp:RowItem   ::= mp:Cell | mp:Meta | OnRowGlitch
+// mp:Cell      ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+// mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+
+
+  // Note that in interfaces below, mork_change parameters kAdd and kNil
+  // both mean about the same thing by default.  Only kCut is interesting,
+  // because this usually means to remove members instead of adding them.
+
+  virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace) = 0;
+  virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
+  virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  
+
+  virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid) = 0;
+  virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
+  virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  
+  virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  
+
+  virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, 
+    const morkMid& inMid, mork_change inChange) = 0;
+  virtual void OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
+  virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  
+
+  virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace,
+    const morkMid& inMid, mork_bool inCutAllRows) = 0;
+  virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+  virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+    
+  virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace) = 0;
+  virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+  virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+  virtual void OnMinusRow(morkEnv* ev) = 0;
+  virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace, 
+    const morkMid& inMid, mork_bool inCutAllCols) = 0;
+  virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos) = 0;  
+  virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
+  virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  
+
+  virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace) = 0;
+  virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
+  virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  
+
+  virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan,
+    const morkMid& inMid) = 0;
+
+  virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+
+  virtual void OnMinusCell(morkEnv* ev) = 0;
+  virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace,
+    const morkMid* inMid, const morkBuf* inBuf) = 0;
+  // Exactly one of inMid and inBuf is nil, and the other is non-nil.
+  // When hex ID syntax is used for a column, then inMid is not nil, and
+  // when a naked string names a column, then inBuf is not nil.
+    
+  virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+  virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) = 0;
+  virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+    
+  virtual void OnValue(morkEnv* ev, const morkSpan& inSpan,
+    const morkBuf& inBuf) = 0;
+
+  virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan,
+    const morkMid& inMid) = 0;
+
+  virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan,
+    const morkMid& inMid) = 0;
+
+  virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan,
+    const morkMid& inMid) = 0;
+  
+// ````` ````` ````` `````   ````` ````` ````` `````  
+protected: // protected parser helper methods
+
+  void ParseChunk(morkEnv* ev); // find parse continuation and resume
+
+  void StartParse(morkEnv* ev); // prepare for parsing
+  void StopParse(morkEnv* ev); // terminate parsing & call needed methods
+
+  int NextChar(morkEnv* ev); // next non-white content
+
+  void OnCellState(morkEnv* ev);
+  void OnMetaState(morkEnv* ev);
+  void OnRowState(morkEnv* ev);
+  void OnTableState(morkEnv* ev);
+  void OnDictState(morkEnv* ev);
+  void OnPortState(morkEnv* ev);
+  void OnStartState(morkEnv* ev);
+  
+  void ReadCell(morkEnv* ev);
+  void ReadRow(morkEnv* ev, int c);
+  void ReadRowPos(morkEnv* ev);
+  void ReadTable(morkEnv* ev);
+  void ReadTableMeta(morkEnv* ev);
+  void ReadDict(morkEnv* ev);
+  mork_bool ReadContent(morkEnv* ev, mork_bool inInsideGroup);
+  void ReadGroup(morkEnv* ev);
+  mork_bool ReadEndGroupId(morkEnv* ev);
+  mork_bool ReadAt(morkEnv* ev, mork_bool inInsideGroup);
+  mork_bool FindGroupEnd(morkEnv* ev);
+  void ReadMeta(morkEnv* ev, int inEndMeta);
+  void ReadAlias(morkEnv* ev);
+  mork_id ReadHex(morkEnv* ev, int* outNextChar);
+  morkBuf* ReadValue(morkEnv* ev);
+  morkBuf* ReadName(morkEnv* ev, int c);
+  mork_bool ReadMid(morkEnv* ev, morkMid* outMid);
+  void ReadDictForm(morkEnv *ev);
+  void ReadCellForm(morkEnv *ev, int c);
+  
+  mork_bool MatchPattern(morkEnv* ev, const char* inPattern);
+  
+  void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan);
+  void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan);
+  void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan);
+  
+  void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan);
+  
+  
+  // void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan)
+  // { MORK_USED_2(ev,ioSpan); }
+  
+  // void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan)
+  // { MORK_USED_2(ev,ioSpan); }
+  
+  // void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan)
+  // { MORK_USED_2(ev,ioSpan); }
+  
+  // void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan)
+  // { MORK_USED_2(ev,ioSpan); }
+  
+  int eat_line_break(morkEnv* ev, int inLast);
+  int eat_line_continue(morkEnv* ev); // last char was '\\'
+  int eat_comment(morkEnv* ev); // last char was '/'
+  
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public non-poly morkParser methods
+    
+  mdb_count ParseMore( // return count of bytes consumed now
+    morkEnv* ev,          // context
+    mork_pos* outPos,     // current byte pos in the stream afterwards
+    mork_bool* outDone,   // is parsing finished?
+    mork_bool* outBroken  // is parsing irreparably dead and broken?
+  );
+  
+  
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakParser(morkParser* me,
+    morkEnv* ev, morkParser** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongParser(morkParser* me,
+    morkEnv* ev, morkParser** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKPARSER_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkPool.cpp
@@ -0,0 +1,589 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKHANDLE_
+#include "morkHandle.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKZONE_
+#include "morkZone.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkPool::CloseMorkNode(morkEnv* ev) // ClosePool() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->ClosePool(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkPool::~morkPool() // assert ClosePool() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkPool::morkPool(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+  nsIMdbHeap* ioSlotHeap)
+: morkNode(inUsage, ioHeap)
+, mPool_Heap( ioSlotHeap )
+, mPool_UsedFramesCount( 0 )
+, mPool_FreeFramesCount( 0 )
+{
+  // mPool_Heap is NOT refcounted
+  MORK_ASSERT(ioSlotHeap);
+  if ( ioSlotHeap )
+    mNode_Derived = morkDerived_kPool;
+}
+
+/*public non-poly*/
+morkPool::morkPool(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkNode(ev, inUsage, ioHeap)
+, mPool_Heap( ioSlotHeap )
+, mPool_UsedFramesCount( 0 )
+, mPool_FreeFramesCount( 0 )
+{
+  if ( ioSlotHeap )
+  {
+    // mPool_Heap is NOT refcounted:
+    // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mPool_Heap);
+    if ( ev->Good() )
+      mNode_Derived = morkDerived_kPool;
+  }
+  else
+    ev->NilPointerError();
+}
+
+/*public non-poly*/ void
+morkPool::ClosePool(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+#ifdef morkZone_CONFIG_ARENA
+#else /*morkZone_CONFIG_ARENA*/
+    //MORK_USED_1(ioZone);
+#endif /*morkZone_CONFIG_ARENA*/
+
+      nsIMdbHeap* heap = mPool_Heap;
+      nsIMdbEnv* mev = ev->AsMdbEnv();
+      morkLink* aLink;
+      morkDeque* d = &mPool_FreeHandleFrames;
+      while ( (aLink = d->RemoveFirst()) != 0 )
+        heap->Free(mev, aLink);
+  
+      // if the pool's closed, get rid of the frames in use too.
+      d = &mPool_UsedHandleFrames;
+      while ( (aLink = d->RemoveFirst()) != 0 )
+        heap->Free(mev, aLink);
+  
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+
+// alloc and free individual instances of handles (inside hand frames):
+morkHandleFace*
+morkPool::NewHandle(morkEnv* ev, mork_size inSize, morkZone* ioZone)
+{
+  void* newBlock = 0;
+  if ( inSize <= sizeof(morkHandleFrame) )
+  {
+    morkLink* firstLink = mPool_FreeHandleFrames.RemoveFirst();
+    if ( firstLink )
+    {
+      newBlock = firstLink;
+      if ( mPool_FreeFramesCount )
+        --mPool_FreeFramesCount;
+      else
+        ev->NewWarning("mPool_FreeFramesCount underflow");
+    }
+    else
+      mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkHandleFrame),
+        (void**) &newBlock);
+  }
+  else
+  {
+    ev->NewWarning("inSize > sizeof(morkHandleFrame)");
+    mPool_Heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &newBlock);
+  }
+#ifdef morkZone_CONFIG_ARENA
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+#endif /*morkZone_CONFIG_ARENA*/
+
+  return (morkHandleFace*) newBlock;
+}
+
+void
+morkPool::ZapHandle(morkEnv* ev, morkHandleFace* ioHandle)
+{
+  if ( ioHandle )
+  {
+    morkLink* handleLink = (morkLink*) ioHandle;
+    mPool_FreeHandleFrames.AddLast(handleLink);
+    ++mPool_FreeFramesCount;
+    // lets free all handles to track down leaks 
+    // - uncomment out next 3 lines, comment out above 2
+//      nsIMdbHeap* heap = mPool_Heap;
+//      nsIMdbEnv* mev = ev->AsMdbEnv();
+//      heap->Free(mev, handleLink);
+ 
+  }
+}
+
+
+// alloc and free individual instances of rows:
+morkRow*
+morkPool::NewRow(morkEnv* ev, morkZone* ioZone) // allocate a new row instance
+{
+  morkRow* newRow = 0;
+  
+#ifdef morkZone_CONFIG_ARENA
+  // a zone 'chip' remembers no size, and so cannot be deallocated:
+  newRow = (morkRow*) ioZone->ZoneNewChip(ev, sizeof(morkRow));
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkRow), (void**) &newRow);
+#endif /*morkZone_CONFIG_ARENA*/
+
+  if ( newRow )
+    MORK_MEMSET(newRow, 0, sizeof(morkRow));
+  
+  return newRow;
+}
+
+void
+morkPool::ZapRow(morkEnv* ev, morkRow* ioRow,
+  morkZone* ioZone) // free old row instance
+{
+#ifdef morkZone_CONFIG_ARENA
+  if ( !ioRow )
+    ev->NilPointerWarning(); // a zone 'chip' cannot be freed
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  if ( ioRow )
+    mPool_Heap->Free(ev->AsMdbEnv(), ioRow);
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+// alloc and free entire vectors of cells (not just one cell at a time)
+morkCell*
+morkPool::NewCells(morkEnv* ev, mork_size inSize,
+  morkZone* ioZone)
+{
+  morkCell* newCells = 0;
+
+  mork_size size = inSize * sizeof(morkCell);
+  if ( size )
+  {
+#ifdef morkZone_CONFIG_ARENA
+    // a zone 'run' knows its size, and can indeed be deallocated:
+    newCells = (morkCell*) ioZone->ZoneNewRun(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+    MORK_USED_1(ioZone);
+    mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newCells);
+#endif /*morkZone_CONFIG_ARENA*/
+  }
+    
+  // note morkAtom depends on having nil stored in all new mCell_Atom slots:
+  if ( newCells )
+    MORK_MEMSET(newCells, 0, size);
+  return newCells;
+}
+
+void
+morkPool::ZapCells(morkEnv* ev, morkCell* ioVector, mork_size inSize,
+  morkZone* ioZone)
+{
+  MORK_USED_1(inSize);
+
+  if ( ioVector )
+  {
+#ifdef morkZone_CONFIG_ARENA
+    // a zone 'run' knows its size, and can indeed be deallocated:
+    ioZone->ZoneZapRun(ev, ioVector);
+#else /*morkZone_CONFIG_ARENA*/
+    MORK_USED_1(ioZone);
+    mPool_Heap->Free(ev->AsMdbEnv(), ioVector);
+#endif /*morkZone_CONFIG_ARENA*/
+  }
+}
+
+// resize (grow or trim) cell vectors inside a containing row instance
+mork_bool
+morkPool::AddRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize,
+  morkZone* ioZone)
+{
+  // note strong implementation similarity to morkArray::Grow()
+
+  MORK_USED_1(ioZone);
+#ifdef morkZone_CONFIG_ARENA
+#else /*morkZone_CONFIG_ARENA*/
+#endif /*morkZone_CONFIG_ARENA*/
+
+  mork_fill fill = ioRow->mRow_Length;
+  if ( ev->Good() && fill < inNewSize ) // need more cells?
+  {
+    morkCell* newCells = this->NewCells(ev, inNewSize, ioZone);
+    if ( newCells )
+    {
+      morkCell* c = newCells; // for iterating during copy
+      morkCell* oldCells = ioRow->mRow_Cells;
+      morkCell* end = oldCells + fill; // copy all the old cells
+      while ( oldCells < end )
+      {
+        *c++ = *oldCells++; // bitwise copy each old cell struct
+      }
+      oldCells = ioRow->mRow_Cells;
+      ioRow->mRow_Cells = newCells;
+      ioRow->mRow_Length = (mork_u2) inNewSize;
+      ++ioRow->mRow_Seed;
+      
+      if ( oldCells )
+        this->ZapCells(ev, oldCells, fill, ioZone);
+    }
+  }
+  return ( ev->Good() && ioRow->mRow_Length >= inNewSize );
+}
+
+mork_bool
+morkPool::CutRowCells(morkEnv* ev, morkRow* ioRow,
+  mork_size inNewSize,
+  morkZone* ioZone)
+{
+  MORK_USED_1(ioZone);
+#ifdef morkZone_CONFIG_ARENA
+#else /*morkZone_CONFIG_ARENA*/
+#endif /*morkZone_CONFIG_ARENA*/
+
+  mork_fill fill = ioRow->mRow_Length;
+  if ( ev->Good() && fill > inNewSize ) // need fewer cells?
+  {
+    if ( inNewSize ) // want any row cells at all?
+    {
+      morkCell* newCells = this->NewCells(ev, inNewSize, ioZone);
+      if ( newCells )
+      {
+        morkCell* saveNewCells = newCells; // Keep newcell pos
+        morkCell* oldCells = ioRow->mRow_Cells;
+        morkCell* oldEnd = oldCells + fill; // one past all old cells
+        morkCell* newEnd = oldCells + inNewSize; // copy only kept old cells
+        while ( oldCells < newEnd )
+        {
+          *newCells++ = *oldCells++; // bitwise copy each old cell struct
+        }
+        while ( oldCells < oldEnd )
+        {
+          if ( oldCells->mCell_Atom ) // need to unref old cell atom?
+            oldCells->SetAtom(ev, (morkAtom*) 0, this); // unref cell atom
+          ++oldCells;
+        }
+        oldCells = ioRow->mRow_Cells;
+        ioRow->mRow_Cells = saveNewCells;
+        ioRow->mRow_Length = (mork_u2) inNewSize;
+        ++ioRow->mRow_Seed;
+        
+        if ( oldCells )
+          this->ZapCells(ev, oldCells, fill, ioZone);
+      }
+    }
+    else // get rid of all row cells
+    {
+      morkCell* oldCells = ioRow->mRow_Cells;
+      ioRow->mRow_Cells = 0;
+      ioRow->mRow_Length = 0;
+      ++ioRow->mRow_Seed;
+      
+      if ( oldCells )
+        this->ZapCells(ev, oldCells, fill, ioZone);
+    }
+  }
+  return ( ev->Good() && ioRow->mRow_Length <= inNewSize );
+}
+
+// alloc & free individual instances of atoms (lots of atom subclasses):
+void
+morkPool::ZapAtom(morkEnv* ev, morkAtom* ioAtom,
+  morkZone* ioZone) // any subclass (by kind)
+{
+#ifdef morkZone_CONFIG_ARENA
+  if ( !ioAtom )
+    ev->NilPointerWarning(); // a zone 'chip' cannot be freed
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  if ( ioAtom )
+    mPool_Heap->Free(ev->AsMdbEnv(), ioAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+morkOidAtom*
+morkPool::NewRowOidAtom(morkEnv* ev, const mdbOid& inOid,
+  morkZone* ioZone)
+{
+  morkOidAtom* newAtom = 0;
+  
+#ifdef morkZone_CONFIG_ARENA
+  // a zone 'chip' remembers no size, and so cannot be deallocated:
+  newAtom = (morkOidAtom*) ioZone->ZoneNewChip(ev, sizeof(morkOidAtom));
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkOidAtom),(void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+
+  if ( newAtom )
+    newAtom->InitRowOidAtom(ev, inOid);
+  return newAtom;
+}
+
+morkOidAtom*
+morkPool::NewTableOidAtom(morkEnv* ev, const mdbOid& inOid,
+  morkZone* ioZone)
+{
+  morkOidAtom* newAtom = 0;
+
+#ifdef morkZone_CONFIG_ARENA
+  // a zone 'chip' remembers no size, and so cannot be deallocated:
+  newAtom = (morkOidAtom*) ioZone->ZoneNewChip(ev, sizeof(morkOidAtom));
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkOidAtom), (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+  if ( newAtom )
+    newAtom->InitTableOidAtom(ev, inOid);
+  return newAtom;
+}
+
+morkAtom*
+morkPool::NewAnonAtom(morkEnv* ev, const morkBuf& inBuf,
+  mork_cscode inForm,
+  morkZone* ioZone)
+// if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee'
+// anon atom will be created, and otherwise a 'big' anon atom.
+{
+  morkAtom* newAtom = 0;
+
+  mork_bool needBig = ( inForm || inBuf.mBuf_Fill > 255 );
+  mork_size size = ( needBig )?
+    morkBigAnonAtom::SizeForFill(inBuf.mBuf_Fill) :
+    morkWeeAnonAtom::SizeForFill(inBuf.mBuf_Fill);
+
+#ifdef morkZone_CONFIG_ARENA
+  // a zone 'chip' remembers no size, and so cannot be deallocated:
+  newAtom = (morkAtom*) ioZone->ZoneNewChip(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+  if ( newAtom )
+  {
+    if ( needBig )
+      ((morkBigAnonAtom*) newAtom)->InitBigAnonAtom(ev, inBuf, inForm);
+    else
+      ((morkWeeAnonAtom*) newAtom)->InitWeeAnonAtom(ev, inBuf);
+  }
+  return newAtom;
+}
+
+morkBookAtom*
+morkPool::NewBookAtom(morkEnv* ev, const morkBuf& inBuf,
+  mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid,
+  morkZone* ioZone)
+// if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee'
+// book atom will be created, and otherwise a 'big' book atom.
+{
+  morkBookAtom* newAtom = 0;
+
+  mork_bool needBig = ( inForm || inBuf.mBuf_Fill > 255 );
+  mork_size size = ( needBig )?
+    morkBigBookAtom::SizeForFill(inBuf.mBuf_Fill) :
+    morkWeeBookAtom::SizeForFill(inBuf.mBuf_Fill);
+
+#ifdef morkZone_CONFIG_ARENA
+  // a zone 'chip' remembers no size, and so cannot be deallocated:
+  newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+  if ( newAtom )
+  {
+    if ( needBig )
+      ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev,
+        inBuf, inForm, ioSpace, inAid);
+    else
+      ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev,
+        inBuf, ioSpace, inAid);
+  }
+  return newAtom;
+}
+
+morkBookAtom*
+morkPool::NewBookAtomCopy(morkEnv* ev, const morkBigBookAtom& inAtom,
+  morkZone* ioZone)
+  // make the smallest kind of book atom that can hold content in inAtom.
+  // The inAtom parameter is often expected to be a staged book atom in
+  // the store, which was used to search an atom space for existing atoms.
+{
+  morkBookAtom* newAtom = 0;
+
+  mork_cscode form = inAtom.mBigBookAtom_Form;
+  mork_fill fill = inAtom.mBigBookAtom_Size;
+  mork_bool needBig = ( form || fill > 255 );
+  mork_size size = ( needBig )?
+    morkBigBookAtom::SizeForFill(fill) :
+    morkWeeBookAtom::SizeForFill(fill);
+
+#ifdef morkZone_CONFIG_ARENA
+  // a zone 'chip' remembers no size, and so cannot be deallocated:
+  newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+  if ( newAtom )
+  {
+    morkBuf buf(inAtom.mBigBookAtom_Body, fill);
+    if ( needBig )
+      ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev,
+        buf, form, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id);
+    else
+      ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev,
+        buf, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id);
+  }
+  return newAtom;
+}
+
+morkBookAtom*
+morkPool::NewFarBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom,
+  morkZone* ioZone)
+  // make the smallest kind of book atom that can hold content in inAtom.
+  // The inAtom parameter is often expected to be a staged book atom in
+  // the store, which was used to search an atom space for existing atoms.
+{
+  morkBookAtom* newAtom = 0;
+
+  mork_cscode form = inAtom.mFarBookAtom_Form;
+  mork_fill fill = inAtom.mFarBookAtom_Size;
+  mork_bool needBig = ( form || fill > 255 );
+  mork_size size = ( needBig )?
+    morkBigBookAtom::SizeForFill(fill) :
+    morkWeeBookAtom::SizeForFill(fill);
+
+#ifdef morkZone_CONFIG_ARENA
+  // a zone 'chip' remembers no size, and so cannot be deallocated:
+  newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+  MORK_USED_1(ioZone);
+  mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+  if ( newAtom )
+  {
+    morkBuf buf(inAtom.mFarBookAtom_Body, fill);
+    if ( needBig )
+      ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev,
+        buf, form, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id);
+    else
+      ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev,
+        buf, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id);
+  }
+  return newAtom;
+}
+
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkPool.h
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKPOOL_
+#define _MORKPOOL_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class morkHandle;
+class morkHandleFrame;
+class morkHandleFace; // just an opaque cookie type
+class morkBigBookAtom;
+class morkFarBookAtom;
+
+#define morkDerived_kPool     /*i*/ 0x706C /* ascii 'pl' */
+
+/*| morkPool: a place to manage pools of non-node objects that are memory
+**| managed out of large chunks of space, so that per-object management
+**| space overhead has no signficant cost.
+|*/
+class morkPool : public morkNode {
+  
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+  nsIMdbHeap*  mPool_Heap; // NON-refcounted heap instance
+  
+  morkDeque    mPool_Blocks;      // linked list of large blocks from heap
+  
+  // These two lists contain instances of morkHandleFrame:
+  morkDeque    mPool_UsedHandleFrames; // handle frames currently being used
+  morkDeque    mPool_FreeHandleFrames; // handle frames currently in free list
+  
+  mork_count   mPool_UsedFramesCount; // length of mPool_UsedHandleFrames
+  mork_count   mPool_FreeFramesCount; // length of mPool_UsedHandleFrames
+    
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // ClosePool() only if open
+  virtual ~morkPool(); // assert that ClosePool() executed earlier
+  
+public: // morkPool construction & destruction
+  morkPool(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+    nsIMdbHeap* ioSlotHeap);
+  morkPool(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+    nsIMdbHeap* ioSlotHeap);
+  void ClosePool(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkPool(const morkPool& other);
+  morkPool& operator=(const morkPool& other);
+
+public: // dynamic type identification
+  mork_bool IsPool() const
+  { return IsNode() && mNode_Derived == morkDerived_kPool; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  void NonPoolTypeError(morkEnv* ev);
+
+public: // morkNode memory management operators
+  void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+  { return morkNode::MakeNew(inSize, ioHeap, ev); }
+  
+  void* operator new(size_t inSize) CPP_THROW_NEW
+  { return ::operator new(inSize); }
+  
+
+public: // other pool methods
+
+  // alloc and free individual instances of handles (inside hand frames):
+  morkHandleFace*  NewHandle(morkEnv* ev, mork_size inSize, morkZone* ioZone);
+  void             ZapHandle(morkEnv* ev, morkHandleFace* ioHandle);
+
+  // alloc and free individual instances of rows:
+  morkRow*  NewRow(morkEnv* ev, morkZone* ioZone); // alloc new row instance
+  void      ZapRow(morkEnv* ev, morkRow* ioRow, morkZone* ioZone); // free old row instance
+
+  // alloc and free entire vectors of cells (not just one cell at a time)
+  morkCell* NewCells(morkEnv* ev, mork_size inSize, morkZone* ioZone);
+  void      ZapCells(morkEnv* ev, morkCell* ioVector, mork_size inSize, morkZone* ioZone);
+  
+  // resize (grow or trim) cell vectors inside a containing row instance
+  mork_bool AddRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, morkZone* ioZone);
+  mork_bool CutRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, morkZone* ioZone);
+  
+  // alloc & free individual instances of atoms (lots of atom subclasses):
+  void ZapAtom(morkEnv* ev, morkAtom* ioAtom, morkZone* ioZone); // any subclass (by kind)
+  
+  morkOidAtom* NewRowOidAtom(morkEnv* ev, const mdbOid& inOid, morkZone* ioZone); 
+  morkOidAtom* NewTableOidAtom(morkEnv* ev, const mdbOid& inOid, morkZone* ioZone);
+  
+  morkAtom* NewAnonAtom(morkEnv* ev, const morkBuf& inBuf,
+    mork_cscode inForm, morkZone* ioZone);
+    // if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee'
+    // anon atom will be created, and otherwise a 'big' anon atom.
+    
+  morkBookAtom* NewBookAtom(morkEnv* ev, const morkBuf& inBuf,
+    mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid, morkZone* ioZone);
+    // if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee'
+    // book atom will be created, and otherwise a 'big' book atom.
+    
+  morkBookAtom* NewBookAtomCopy(morkEnv* ev, const morkBigBookAtom& inAtom, morkZone* ioZone);
+    // make the smallest kind of book atom that can hold content in inAtom.
+    // The inAtom parameter is often expected to be a staged book atom in
+    // the store, which was used to search an atom space for existing atoms.
+    
+  morkBookAtom* NewFarBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom, morkZone* ioZone);
+    // make the smallest kind of book atom that can hold content in inAtom.
+    // The inAtom parameter is often expected to be a staged book atom in
+    // the store, which was used to search an atom space for existing atoms.
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakPool(morkPool* me,
+    morkEnv* ev, morkPool** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongPool(morkPool* me,
+    morkEnv* ev, morkPool** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKPOOL_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkPortTableCursor.cpp
@@ -0,0 +1,468 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKPORTTABLECURSOR_
+#include "morkPortTableCursor.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkPortTableCursor::CloseMorkNode(morkEnv* ev) // ClosePortTableCursor() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->ClosePortTableCursor(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkPortTableCursor::~morkPortTableCursor() // ClosePortTableCursor() executed earlier
+{
+  CloseMorkNode(mMorkEnv);
+}
+
+/*public non-poly*/
+morkPortTableCursor::morkPortTableCursor(morkEnv* ev,
+  const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, morkStore* ioStore, mdb_scope inRowScope,
+  mdb_kind inTableKind, nsIMdbHeap* ioSlotHeap)
+: morkCursor(ev, inUsage, ioHeap)
+, mPortTableCursor_Store( 0 )
+, mPortTableCursor_RowScope( (mdb_scope) -1 ) // we want != inRowScope
+, mPortTableCursor_TableKind( (mdb_kind) -1 ) // we want != inTableKind
+, mPortTableCursor_LastTable ( 0 ) // not refcounted
+, mPortTableCursor_RowSpace( 0 ) // strong ref to row space
+, mPortTableCursor_TablesDidEnd( morkBool_kFalse )
+, mPortTableCursor_SpacesDidEnd( morkBool_kFalse )
+{
+  if ( ev->Good() )
+  {
+    if ( ioStore && ioSlotHeap )
+    {
+      mCursor_Pos = -1;
+      mCursor_Seed = 0; // let the iterator do its own seed handling
+      morkStore::SlotWeakStore(ioStore, ev, &mPortTableCursor_Store);
+
+      if ( this->SetRowScope(ev, inRowScope) )
+        this->SetTableKind(ev, inTableKind);
+        
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kPortTableCursor;
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkPortTableCursor, morkCursor, nsIMdbPortTableCursor)
+
+morkEnv*
+morkPortTableCursor::CanUsePortTableCursor(nsIMdbEnv* mev,
+  mork_bool inMutable, mdb_err* outErr) const
+{
+  morkEnv* outEnv = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( IsPortTableCursor() )
+      outEnv = ev;
+    else
+      NonPortTableCursorTypeError(ev);
+    *outErr = ev->AsErr();
+  }
+  MORK_ASSERT(outEnv);
+  return outEnv;
+}
+
+
+/*public non-poly*/ void
+morkPortTableCursor::ClosePortTableCursor(morkEnv* ev) 
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mCursor_Pos = -1;
+      mCursor_Seed = 0;
+      mPortTableCursor_LastTable = 0;
+      morkStore::SlotWeakStore((morkStore*) 0, ev, &mPortTableCursor_Store);
+      morkRowSpace::SlotStrongRowSpace((morkRowSpace*) 0, ev,
+        &mPortTableCursor_RowSpace);
+      this->CloseCursor(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkPortTableCursor::NilCursorStoreError(morkEnv* ev)
+{
+  ev->NewError("nil mPortTableCursor_Store");
+}
+
+/*static*/ void
+morkPortTableCursor::NonPortTableCursorTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkPortTableCursor");
+}
+
+mork_bool 
+morkPortTableCursor::SetRowScope(morkEnv* ev, mork_scope inRowScope)
+{
+  mPortTableCursor_RowScope = inRowScope;
+  mPortTableCursor_LastTable = 0; // restart iteration of space
+  
+  mPortTableCursor_TableIter.CloseMapIter(ev);
+  mPortTableCursor_TablesDidEnd = morkBool_kTrue;
+  mPortTableCursor_SpacesDidEnd = morkBool_kTrue;
+  
+  morkStore* store = mPortTableCursor_Store;
+  if ( store )
+  {
+    morkRowSpace* space = mPortTableCursor_RowSpace;
+
+    if ( inRowScope ) // intend to cover a specific scope only?
+    {
+      space = store->LazyGetRowSpace(ev, inRowScope);
+      morkRowSpace::SlotStrongRowSpace(space, ev, 
+       &mPortTableCursor_RowSpace);
+       
+      // We want mPortTableCursor_SpacesDidEnd == morkBool_kTrue
+      // to show this is the only space to be covered.
+    }
+    else // prepare space map iter to cover all space scopes
+    {
+      morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter;
+      rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);
+      
+      space = 0;
+      (void) rsi->FirstRowSpace(ev, (mork_scope*) 0, &space);
+      morkRowSpace::SlotStrongRowSpace(space, ev,
+        &mPortTableCursor_RowSpace);
+        
+      if ( space ) // found first space in store
+        mPortTableCursor_SpacesDidEnd = morkBool_kFalse;
+    }
+
+    this->init_space_tables_map(ev);
+  }
+  else
+    this->NilCursorStoreError(ev);
+    
+  return ev->Good();
+}
+
+void
+morkPortTableCursor::init_space_tables_map(morkEnv* ev)
+{
+  morkRowSpace* space = mPortTableCursor_RowSpace;
+  if ( space && ev->Good() )
+  {
+    morkTableMapIter* ti = &mPortTableCursor_TableIter;
+    ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
+    if ( ev->Good() )
+      mPortTableCursor_TablesDidEnd = morkBool_kFalse;
+  }
+}
+
+
+mork_bool
+morkPortTableCursor::SetTableKind(morkEnv* ev, mork_kind inTableKind)
+{
+  mPortTableCursor_TableKind = inTableKind;
+  mPortTableCursor_LastTable = 0; // restart iteration of space
+
+  mPortTableCursor_TablesDidEnd = morkBool_kTrue;
+
+  morkRowSpace* space = mPortTableCursor_RowSpace;
+  if ( !space && mPortTableCursor_RowScope == 0 )
+  {
+    this->SetRowScope(ev, 0);
+    space = mPortTableCursor_RowSpace;
+  }
+  this->init_space_tables_map(ev);
+  
+  return ev->Good();
+}
+
+morkRowSpace*
+morkPortTableCursor::NextSpace(morkEnv* ev)
+{
+  morkRowSpace* outSpace = 0;
+  mPortTableCursor_LastTable = 0;
+  mPortTableCursor_SpacesDidEnd = morkBool_kTrue;
+  mPortTableCursor_TablesDidEnd = morkBool_kTrue;
+
+  if ( !mPortTableCursor_RowScope ) // not just one scope?
+  {
+    morkStore* store = mPortTableCursor_Store;
+    if ( store )
+    {
+      morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter;
+
+      (void) rsi->NextRowSpace(ev, (mork_scope*) 0, &outSpace);
+      morkRowSpace::SlotStrongRowSpace(outSpace, ev,
+        &mPortTableCursor_RowSpace);
+        
+      if ( outSpace ) // found next space in store
+      {
+        mPortTableCursor_SpacesDidEnd = morkBool_kFalse;
+        
+        this->init_space_tables_map(ev);
+
+        if ( ev->Bad() )
+          outSpace = 0;
+      }
+    }
+    else
+      this->NilCursorStoreError(ev);
+  }
+    
+  return outSpace;
+}
+
+morkTable *
+morkPortTableCursor::NextTable(morkEnv* ev)
+{
+  mork_kind kind = mPortTableCursor_TableKind;
+  
+  do // until spaces end, or until we find a table in a space
+  { 
+    morkRowSpace* space = mPortTableCursor_RowSpace;
+    if ( mPortTableCursor_TablesDidEnd ) // current space exhausted?
+      space = this->NextSpace(ev); // go on to the next space
+      
+    if ( space ) // have a space remaining that might hold tables?
+    {
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+      morkTableMapIter* ti = &mPortTableCursor_TableIter;
+      morkTable* table = ( mPortTableCursor_LastTable )?
+        ti->NextTable(ev) : ti->FirstTable(ev);
+
+      for ( ; table && ev->Good(); table = ti->NextTable(ev) )
+      
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+      mork_tid* key = 0; // ignore keys in table map
+      morkTable* table = 0; // old value table in the map
+      morkTableMapIter* ti = &mPortTableCursor_TableIter;
+      mork_change* c = ( mPortTableCursor_LastTable )?
+        ti->NextTable(ev, key, &table) : ti->FirstTable(ev, key, &table);
+
+      for ( ; c && ev->Good(); c = ti->NextTable(ev, key, &table) )
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+      {
+        if ( table && table->IsTable() )
+        {
+          if ( !kind || kind == table->mTable_Kind )
+          {
+            mPortTableCursor_LastTable = table; // ti->NextTable() hence
+            return table;
+          }
+        }
+        else
+          table->NonTableTypeWarning(ev);
+      }
+      mPortTableCursor_TablesDidEnd = morkBool_kTrue; // space is done
+      mPortTableCursor_LastTable = 0; // make sure next space starts fresh
+    }
+  
+  } while ( ev->Good() && !mPortTableCursor_SpacesDidEnd );
+
+  return (morkTable*) 0;
+}
+
+
+// { ----- begin table iteration methods -----
+
+// { ===== begin nsIMdbPortTableCursor methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkPortTableCursor::SetPort(nsIMdbEnv* mev, nsIMdbPort* ioPort)
+{
+  NS_ASSERTION(PR_FALSE,"not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkPortTableCursor::GetPort(nsIMdbEnv* mev, nsIMdbPort** acqPort)
+{
+  mdb_err outErr = 0;
+  nsIMdbPort* outPort = 0;
+  morkEnv* ev =
+    this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    if ( mPortTableCursor_Store )
+      outPort = mPortTableCursor_Store->AcquireStoreHandle(ev);
+    outErr = ev->AsErr();
+  }
+  if ( acqPort )
+    *acqPort = outPort;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkPortTableCursor::SetRowScope(nsIMdbEnv* mev, // sets pos to -1
+  mdb_scope inRowScope)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev =
+    this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    mCursor_Pos = -1;
+    
+    SetRowScope(ev, inRowScope);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkPortTableCursor::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope)
+{
+  mdb_err outErr = 0;
+  mdb_scope rowScope = 0;
+  morkEnv* ev =
+    this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    rowScope = mPortTableCursor_RowScope;
+    outErr = ev->AsErr();
+  }
+  *outRowScope = rowScope;
+  return outErr;
+}
+// setting row scope to zero iterates over all row scopes in port
+  
+NS_IMETHODIMP
+morkPortTableCursor::SetTableKind(nsIMdbEnv* mev, // sets pos to -1
+  mdb_kind inTableKind)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev =
+    this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    mCursor_Pos = -1;
+    
+    SetTableKind(ev, inTableKind);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkPortTableCursor::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind)
+// setting table kind to zero iterates over all table kinds in row scope
+{
+  mdb_err outErr = 0;
+  mdb_kind tableKind = 0;
+  morkEnv* ev =
+    this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    tableKind = mPortTableCursor_TableKind;
+    outErr = ev->AsErr();
+  }
+  *outTableKind = tableKind;
+  return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin table iteration methods -----
+NS_IMETHODIMP
+morkPortTableCursor::NextTable( // get table at next position in the db
+  nsIMdbEnv* mev, // context
+  nsIMdbTable** acqTable)
+{
+  mdb_err outErr = 0;
+  nsIMdbTable* outTable = 0;
+  morkEnv* ev =
+    CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkTable* table = NextTable(ev);
+    if ( table && ev->Good() )
+      outTable = table->AcquireTableHandle(ev);
+        
+    outErr = ev->AsErr();
+  }
+  if ( acqTable )
+    *acqTable = outTable;
+  return outErr;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkPortTableCursor.h
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKPORTTABLECURSOR_
+#define _MORKPORTTABLECURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class orkinPortTableCursor;
+#define morkDerived_kPortTableCursor  /*i*/ 0x7443 /* ascii 'tC' */
+
+class morkPortTableCursor : public morkCursor, public nsIMdbPortTableCursor { // row iterator
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+  // morkFactory* mObject_Factory;  // weak ref to suite factory
+
+  // mork_seed  mCursor_Seed;
+  // mork_pos   mCursor_Pos;
+  // mork_bool  mCursor_DoFailOnSeedOutOfSync;
+  // mork_u1    mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+  // { ----- begin attribute methods -----
+  NS_IMETHOD SetPort(nsIMdbEnv* ev, nsIMdbPort* ioPort); // sets pos to -1
+  NS_IMETHOD GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort);
+  
+  NS_IMETHOD SetRowScope(nsIMdbEnv* ev, // sets pos to -1
+    mdb_scope inRowScope);
+  NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope); 
+  // setting row scope to zero iterates over all row scopes in port
+    
+  NS_IMETHOD SetTableKind(nsIMdbEnv* ev, // sets pos to -1
+    mdb_kind inTableKind);
+  NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind);
+  // setting table kind to zero iterates over all table kinds in row scope
+  // } ----- end attribute methods -----
+
+  // { ----- begin table iteration methods -----
+  NS_IMETHOD NextTable( // get table at next position in the db
+    nsIMdbEnv* ev, // context
+    nsIMdbTable** acqTable); // the next table in the iteration
+  // } ----- end table iteration methods -----
+  morkStore*    mPortTableCursor_Store;  // weak ref to store
+  
+  mdb_scope     mPortTableCursor_RowScope;
+  mdb_kind      mPortTableCursor_TableKind;
+  
+  // We only care if LastTable is non-nil, so it is not refcounted;
+  // so you must never access table state or methods using LastTable:
+  
+  morkTable* mPortTableCursor_LastTable; // nil or last table (no refcount)
+  morkRowSpace* mPortTableCursor_RowSpace; // current space (strong ref)
+
+  morkRowSpaceMapIter mPortTableCursor_SpaceIter; // iter over spaces
+  morkTableMapIter    mPortTableCursor_TableIter; // iter over tables 
+  
+  // these booleans indicate when the table or space iterator is exhausted:
+  
+  mork_bool           mPortTableCursor_TablesDidEnd; // no more tables?
+  mork_bool           mPortTableCursor_SpacesDidEnd; // no more spaces?
+  mork_u1             mPortTableCursor_Pad[ 2 ]; // for u4 alignment
+   
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // ClosePortTableCursor()
+  virtual ~morkPortTableCursor(); // assert that close executed earlier
+  
+public: // morkPortTableCursor construction & destruction
+  morkPortTableCursor(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkStore* ioStore, mdb_scope inRowScope,
+      mdb_kind inTableKind, nsIMdbHeap* ioSlotHeap);
+  void ClosePortTableCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkPortTableCursor(const morkPortTableCursor& other);
+  morkPortTableCursor& operator=(const morkPortTableCursor& other);
+
+public: // dynamic type identification
+  mork_bool IsPortTableCursor() const
+  { return IsNode() && mNode_Derived == morkDerived_kPortTableCursor; }
+// } ===== end morkNode methods =====
+
+protected: // utilities
+
+  void init_space_tables_map(morkEnv* ev);
+
+public: // other cursor methods
+
+  static void NilCursorStoreError(morkEnv* ev);
+  static void NonPortTableCursorTypeError(morkEnv* ev);
+
+ morkEnv* CanUsePortTableCursor(nsIMdbEnv* mev,
+  mork_bool inMutable, mdb_err* outErr) const;
+
+  
+  morkRowSpace* NextSpace(morkEnv* ev);
+  morkTable* NextTable(morkEnv* ev);
+
+  mork_bool SetRowScope(morkEnv* ev, mork_scope inRowScope);
+  mork_bool SetTableKind(morkEnv* ev, mork_kind inTableKind);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakPortTableCursor(morkPortTableCursor* me,
+    morkEnv* ev, morkPortTableCursor** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongPortTableCursor(morkPortTableCursor* me,
+    morkEnv* ev, morkPortTableCursor** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKPORTTABLECURSOR_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkProbeMap.cpp
@@ -0,0 +1,1241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This code is a port to NS Mork from public domain Mithril C++ sources.
+// Note many code comments here come verbatim from cut-and-pasted Mithril.
+// In many places, code is identical; Mithril versions stay public domain.
+// Changes in porting are mainly class type and scalar type name changes.
+
+#include "nscore.h"
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+/*============================================================================*/
+/* morkMapScratch */
+
+void morkMapScratch::halt_map_scratch(morkEnv* ev)
+{
+  nsIMdbHeap* heap = sMapScratch_Heap;
+  
+  if ( heap )
+  {
+    if ( sMapScratch_Keys )
+      heap->Free(ev->AsMdbEnv(), sMapScratch_Keys);
+    if ( sMapScratch_Vals )
+      heap->Free(ev->AsMdbEnv(), sMapScratch_Vals);
+  }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+/*============================================================================*/
+/* morkProbeMap */
+
+void morkProbeMap::ProbeMapBadTagError(morkEnv* ev) const
+{
+  ev->NewError("bad sProbeMap_Tag");
+  if ( !this )
+    ev->NewError("nil morkProbeMap");
+}
+
+void morkProbeMap::WrapWithNoVoidSlotError(morkEnv* ev) const
+{
+  ev->NewError("wrap without void morkProbeMap slot");
+}
+
+void morkProbeMap::GrowFailsMaxFillError(morkEnv* ev) const
+{
+  ev->NewError("grow fails morkEnv > sMap_Fill");
+}
+
+void morkProbeMap::MapKeyIsNotIPError(morkEnv* ev) const
+{
+  ev->NewError("not sMap_KeyIsIP");
+}
+
+void morkProbeMap::MapValIsNotIPError(morkEnv* ev) const
+{
+  ev->NewError("not sMap_ValIsIP");
+}
+
+void morkProbeMap::rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch)
+{
+  mork_size keySize = sMap_KeySize; // size of every key bucket
+  mork_size valSize = sMap_ValSize; // size of every associated value
+  
+  mork_count slots = sMap_Slots; // number of new buckets
+  mork_u1* keys = sMap_Keys; // destination for rehashed keys
+  mork_u1* vals = sMap_Vals; // destination for any copied values
+  
+  mork_bool keyIsIP = ( keys && keySize == sizeof(mork_ip) && sMap_KeyIsIP );
+  mork_bool valIsIP = ( vals && valSize == sizeof(mork_ip) && sMap_ValIsIP );
+
+  mork_count oldSlots = ioScratch->sMapScratch_Slots; // sMap_Slots
+  mork_u1* oldKeys = ioScratch->sMapScratch_Keys; // sMap_Keys
+  mork_u1* oldVals = ioScratch->sMapScratch_Vals; // sMap_Vals
+  mork_u1* end = oldKeys + (keySize * oldSlots); // one byte past last key
+  
+  mork_fill fill = 0; // let's count the actual fill for a double check
+  
+  while ( oldKeys < end ) // another old key bucket to rehash if non-nil?
+  {
+    if ( !this->ProbeMapIsKeyNil(ev, oldKeys) ) // need to rehash?
+    {
+      ++fill; // this had better match sMap_Fill when we are all done
+      mork_u4 hash = this->ProbeMapHashMapKey(ev, oldKeys);
+
+      mork_pos i = hash % slots;   // target hash bucket
+      mork_pos startPos = i;       // remember start to detect
+      
+      mork_u1* k = keys + (i * keySize);
+      while ( !this->ProbeMapIsKeyNil(ev, k) )
+      {
+        if ( ++i >= (mork_pos)slots ) // advanced past end? need to wrap around now?
+          i = 0; // wrap around to first slot in map's hash table
+          
+        if ( i == startPos ) // no void slots were found anywhere in map?
+        {
+          this->WrapWithNoVoidSlotError(ev); // should never happen
+          return; // this is bad, and we can't go on with the rehash
+        }
+        k = keys + (i * keySize);
+      }
+      if ( keyIsIP ) // int special case?
+        *((mork_ip*) k) = *((const mork_ip*) oldKeys); // fast bitwise copy
+      else
+        MORK_MEMCPY(k, oldKeys, keySize); // slow bitwise copy
+
+      if ( oldVals ) // need to copy values as well?
+      {
+        mork_size valOffset = (i * valSize);
+        mork_u1* v = vals + valOffset;
+        mork_u1* ov = oldVals + valOffset;
+        if ( valIsIP ) // int special case?
+          *((mork_ip*) v) = *((const mork_ip*) ov); // fast bitwise copy
+        else
+          MORK_MEMCPY(v, ov, valSize); // slow bitwise copy
+      }
+    }
+    oldKeys += keySize; // advance to next key bucket in old map
+  }
+  if ( fill != sMap_Fill ) // is the recorded value of sMap_Fill wrong?
+  {
+    ev->NewWarning("fill != sMap_Fill");
+    sMap_Fill = fill;
+  }
+}
+
+mork_bool morkProbeMap::grow_probe_map(morkEnv* ev)
+{
+  if ( sMap_Heap ) // can we grow the map?
+  {
+    mork_num newSlots = ((sMap_Slots * 4) / 3) + 1; // +25%
+    morkMapScratch old; // a place to temporarily hold all the old arrays
+    if ( this->new_slots(ev, &old, newSlots) ) // have more?
+    {      
+      ++sMap_Seed; // note the map has changed
+      this->rehash_old_map(ev, &old);
+      
+      if ( ev->Good() ) 
+      {
+        mork_count slots = sMap_Slots;
+        mork_num emptyReserve = (slots / 7) + 1; // keep this many empty
+        mork_fill maxFill = slots - emptyReserve; // new max occupancy
+        if ( maxFill > sMap_Fill ) // new max is bigger than old occupancy?
+          sProbeMap_MaxFill = maxFill; // we can install new max for fill
+        else
+          this->GrowFailsMaxFillError(ev); // we have invariant failure
+      }
+      
+      if ( ev->Bad() ) // rehash failed? need to revert map to last state?
+        this->revert_map(ev, &old); // swap the vectors back again
+
+      old.halt_map_scratch(ev); // remember to free the old arrays
+    }
+  }
+  else ev->OutOfMemoryError();
+  
+  return ev->Good();
+}
+
+void morkProbeMap::revert_map(morkEnv* ev, morkMapScratch* ioScratch)
+{
+  mork_count tempSlots = ioScratch->sMapScratch_Slots; // sMap_Slots  
+  mork_u1* tempKeys = ioScratch->sMapScratch_Keys;     // sMap_Keys
+  mork_u1* tempVals = ioScratch->sMapScratch_Vals;     // sMap_Vals
+  
+  ioScratch->sMapScratch_Slots = sMap_Slots;
+  ioScratch->sMapScratch_Keys = sMap_Keys;
+  ioScratch->sMapScratch_Vals = sMap_Vals;
+  
+  sMap_Slots = tempSlots;
+  sMap_Keys = tempKeys;
+  sMap_Vals = tempVals;
+}
+
+void morkProbeMap::put_probe_kv(morkEnv* ev,
+  const void* inAppKey, const void* inAppVal, mork_pos inPos)
+{
+  mork_u1* mapVal = 0;
+  mork_u1* mapKey = 0;
+
+  mork_num valSize = sMap_ValSize;
+  if ( valSize && inAppVal ) // map holds values? caller sends value?
+  {
+    mork_u1* val = sMap_Vals + (valSize * inPos);
+    if ( valSize == sizeof(mork_ip) && sMap_ValIsIP ) // int special case? 
+      *((mork_ip*) val) = *((const mork_ip*) inAppVal);
+    else
+      mapVal = val; // show possible need to call ProbeMapPushIn()
+  }
+  if ( inAppKey ) // caller sends the key? 
+  {
+    mork_num keySize = sMap_KeySize;
+    mork_u1* key = sMap_Keys + (keySize * inPos);
+    if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP ) // int special case? 
+      *((mork_ip*) key) = *((const mork_ip*) inAppKey);
+    else
+      mapKey = key; // show possible need to call ProbeMapPushIn()
+  }
+  else
+    ev->NilPointerError();
+
+  if ( (  inAppVal && mapVal ) || ( inAppKey && mapKey ) )
+    this->ProbeMapPushIn(ev, inAppKey, inAppVal, mapKey, mapVal);
+
+  if ( sMap_Fill > sProbeMap_MaxFill )
+    this->grow_probe_map(ev);
+}
+
+void morkProbeMap::get_probe_kv(morkEnv* ev,
+  void* outAppKey, void* outAppVal, mork_pos inPos) const
+{
+  const mork_u1* mapVal = 0;
+  const mork_u1* mapKey = 0;
+
+  mork_num valSize = sMap_ValSize;
+  if ( valSize && outAppVal ) // map holds values? caller wants value?
+  {
+    const mork_u1* val = sMap_Vals + (valSize * inPos);
+    if ( valSize == sizeof(mork_ip) && sMap_ValIsIP ) // int special case? 
+      *((mork_ip*) outAppVal) = *((const mork_ip*) val);
+    else
+      mapVal = val; // show possible need to call ProbeMapPullOut()
+  }
+  if ( outAppKey ) // caller wants the key? 
+  {
+    mork_num keySize = sMap_KeySize;
+    const mork_u1* key = sMap_Keys + (keySize * inPos);
+    if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP ) // int special case? 
+      *((mork_ip*) outAppKey) = *((const mork_ip*) key);
+    else
+      mapKey = key; // show possible need to call ProbeMapPullOut()
+  }
+  if ( ( outAppVal && mapVal ) || ( outAppKey && mapKey ) )
+    this->ProbeMapPullOut(ev, mapKey, mapVal, outAppKey, outAppVal);
+}
+
+mork_test
+morkProbeMap::find_key_pos(morkEnv* ev, const void* inAppKey,
+  mork_u4 inHash, mork_pos* outPos) const
+{
+  mork_u1* k = sMap_Keys;        // array of keys, each of size sMap_KeySize
+  mork_num size = sMap_KeySize;  // number of bytes in each key
+  mork_count slots = sMap_Slots; // total number of key buckets
+  mork_pos i = inHash % slots;   // target hash bucket
+  mork_pos startPos = i;         // remember start to detect
+  
+  mork_test outTest = this->MapTest(ev, k + (i * size), inAppKey);
+  while ( outTest == morkTest_kMiss )
+  {
+    if ( ++i >= (mork_pos)slots ) // advancing goes beyond end? need to wrap around now?
+      i = 0; // wrap around to first slot in map's hash table
+      
+    if ( i == startPos ) // no void slots were found anywhere in map?
+    {
+      this->WrapWithNoVoidSlotError(ev); // should never happen
+      break; // end loop on kMiss; note caller expects either kVoid or kHit
+    }
+    outTest = this->MapTest(ev, k + (i * size), inAppKey);
+  }
+  *outPos = i;
+  
+  return outTest;
+}
+ 
+void morkProbeMap::probe_map_lazy_init(morkEnv* ev)
+{
+  if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action?
+  {
+    // The constructor cannot successfully call virtual ProbeMapClearKey(),
+    // so we lazily do so now, when we add the first member to the map.
+    
+    mork_u1* keys = sMap_Keys;
+    if ( keys ) // okay to call lazy virtual clear method on new map keys?
+    {
+      if ( sProbeMap_ZeroIsClearKey ) // zero is good enough to clear keys?
+      {
+        mork_num keyVolume = sMap_Slots * sMap_KeySize;
+        if ( keyVolume )
+          MORK_MEMSET(keys, 0, keyVolume);
+      }
+      else
+        this->ProbeMapClearKey(ev, keys, sMap_Slots);
+    }
+    else
+      this->MapNilKeysError(ev);
+  }
+  sProbeMap_LazyClearOnAdd = 0; // don't do this ever again
+}
+
+mork_bool
+morkProbeMap::MapAtPut(morkEnv* ev,
+  const void* inAppKey, const void* inAppVal,
+  void* outAppKey, void* outAppVal)
+{
+  mork_bool outPut = morkBool_kFalse;
+  
+  if ( this->GoodProbeMap() ) /* looks good? */
+  {
+    if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action?
+      this->probe_map_lazy_init(ev);
+          
+    if ( ev->Good() )
+    {
+      mork_pos slotPos = 0;
+      mork_u4 hash = this->MapHash(ev, inAppKey);
+      mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos);
+      outPut = ( test == morkTest_kHit );
+
+      if ( outPut ) // replacing an old assoc? no change in member count?
+      {
+        if ( outAppKey || outAppVal ) /* copy old before cobber? */
+          this->get_probe_kv(ev, outAppKey, outAppVal, slotPos);
+      }
+      else // adding a new assoc increases membership by one
+      {
+        ++sMap_Fill; /* one more member in the collection */
+      }
+      
+      if ( test != morkTest_kMiss ) /* found slot to hold new assoc? */
+      {
+        ++sMap_Seed; /* note the map has changed */
+        this->put_probe_kv(ev, inAppKey, inAppVal, slotPos);
+      }
+    }
+  }
+  else this->ProbeMapBadTagError(ev);
+  
+  return outPut;
+}
+    
+mork_bool
+morkProbeMap::MapAt(morkEnv* ev, const void* inAppKey,
+    void* outAppKey, void* outAppVal)
+{
+  if ( this->GoodProbeMap() ) /* looks good? */
+  {
+    if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action?
+      this->probe_map_lazy_init(ev);
+          
+    mork_pos slotPos = 0;
+    mork_u4 hash = this->MapHash(ev, inAppKey);
+    mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos);
+    if ( test == morkTest_kHit ) /* found an assoc pair for inAppKey? */
+    {
+      this->get_probe_kv(ev, outAppKey, outAppVal, slotPos);
+      return morkBool_kTrue;
+    }
+  }
+  else this->ProbeMapBadTagError(ev);
+  
+  return morkBool_kFalse;
+}
+    
+mork_num
+morkProbeMap::MapCutAll(morkEnv* ev)
+{
+  mork_num outCutAll = 0;
+  
+  if ( this->GoodProbeMap() ) /* looks good? */
+  {
+    outCutAll = sMap_Fill; /* number of members cut, which is all of them */
+    
+    if ( sMap_Keys && !sProbeMap_ZeroIsClearKey )
+      this->ProbeMapClearKey(ev, sMap_Keys, sMap_Slots);
+
+    sMap_Fill = 0; /* map now has no members */
+  }
+  else this->ProbeMapBadTagError(ev);
+  
+  return outCutAll;
+}
+    
+// { ===== node interface =====
+
+/*virtual*/
+morkProbeMap::~morkProbeMap() // assert NodeStop() finished earlier
+{
+  MORK_ASSERT(sMap_Keys==0);
+  MORK_ASSERT(sProbeMap_Tag==0);
+}
+
+/*public virtual*/ void
+morkProbeMap::CloseMorkNode(morkEnv* ev) // CloseMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseProbeMap(ev);
+    this->MarkShut();
+  }
+}
+
+void morkProbeMap::CloseProbeMap(morkEnv* ev)
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      nsIMdbHeap* heap = sMap_Heap;
+      if ( heap ) // able to free map arrays?
+      {
+        void* block = sMap_Keys;
+        if ( block )
+        {
+          heap->Free(ev->AsMdbEnv(), block);
+          sMap_Keys = 0;
+        }
+          
+        block = sMap_Vals;
+        if ( block )
+        {
+          heap->Free(ev->AsMdbEnv(), block);
+          sMap_Vals = 0;
+        }
+      }
+      sMap_Keys = 0;
+      sMap_Vals = 0;
+      
+      this->CloseNode(ev);
+      sProbeMap_Tag = 0;
+      sProbeMap_MaxFill = 0;
+      
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+void*
+morkProbeMap::clear_alloc(morkEnv* ev, mork_size inSize)
+{
+  void* p = 0;
+  nsIMdbHeap* heap = sMap_Heap;
+  if ( heap )
+  {
+    if ( heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p) == 0 && p )
+    {
+      MORK_MEMSET(p, 0, inSize);
+      return p;
+    }
+  }
+  else
+    ev->NilPointerError();
+    
+  return (void*) 0;
+}
+
+/*| map_new_keys: allocate an array of inSlots new keys filled with zero.
+**| (cf IronDoc's FeHashTable_new_keys())
+|*/
+mork_u1*
+morkProbeMap::map_new_keys(morkEnv* ev, mork_num inSlots)
+{
+  mork_num size = inSlots * sMap_KeySize;
+  return (mork_u1*) this->clear_alloc(ev, size);
+}
+
+/*| map_new_vals: allocate an array of inSlots new values filled with zero.
+**| When values are zero sized, we just return a null pointer.
+**|
+**| (cf IronDoc's FeHashTable_new_values())
+|*/
+mork_u1*
+morkProbeMap::map_new_vals(morkEnv* ev, mork_num inSlots)
+{
+  mork_u1* values = 0;
+  mork_num size = inSlots * sMap_ValSize;
+  if ( size )
+    values = (mork_u1*) this->clear_alloc(ev, size);
+  return values;
+}
+
+
+void morkProbeMap::MapSeedOutOfSyncError(morkEnv* ev)
+{
+  ev->NewError("sMap_Seed out of sync");
+}
+
+void morkProbeMap::MapFillUnderflowWarning(morkEnv* ev)
+{
+  ev->NewWarning("sMap_Fill underflow");
+}
+
+void morkProbeMap::MapNilKeysError(morkEnv* ev)
+{
+  ev->NewError("nil sMap_Keys");
+}
+
+void morkProbeMap::MapZeroKeySizeError(morkEnv* ev)
+{
+  ev->NewError("zero sMap_KeySize");
+}
+
+/*static*/
+void morkProbeMap::ProbeMapCutError(morkEnv* ev)
+{
+  ev->NewError("morkProbeMap cannot cut");
+}
+
+
+void morkProbeMap::init_probe_map(morkEnv* ev, mork_size inSlots)
+{
+  // Note we cannot successfully call virtual ProbeMapClearKey() when we
+  // call init_probe_map() inside the constructor; so we leave this problem
+  // to the caller.  (The constructor will call ProbeMapClearKey() later
+  // after setting a suitable lazy flag to show this action is pending.)
+
+  if ( ev->Good() )
+  {
+    morkMapScratch old;
+
+    if ( inSlots < 7 ) // capacity too small?
+      inSlots = 7; // increase to reasonable minimum
+    else if ( inSlots > (128 * 1024) ) // requested capacity too big?
+      inSlots = (128 * 1024); // decrease to reasonable maximum
+      
+    if ( this->new_slots(ev, &old, inSlots) )
+      sProbeMap_Tag = morkProbeMap_kTag;
+      
+    mork_count slots = sMap_Slots;
+    mork_num emptyReserve = (slots / 7) + 1; // keep this many empty
+    sProbeMap_MaxFill = slots - emptyReserve;
+
+    MORK_MEMSET(&old, 0, sizeof(morkMapScratch)); // don't bother halting
+  }
+}
+
+mork_bool
+morkProbeMap::new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots)
+{
+  mork_bool outNew = morkBool_kFalse;
+  
+  // Note we cannot successfully call virtual ProbeMapClearKey() when we
+  // call new_slots() inside the constructor; so we leave this problem
+  // to the caller.  (The constructor will call ProbeMapClearKey() later
+  // after setting a suitable lazy flag to show this action is pending.)
+    
+  // allocate every new array before we continue:
+  mork_u1* newKeys = this->map_new_keys(ev, inSlots);
+  mork_u1* newVals = this->map_new_vals(ev, inSlots);
+  
+  // okay for newVals to be null when values are zero sized?
+  mork_bool okayValues = ( newVals || !sMap_ValSize );
+  
+  if ( newKeys && okayValues )
+  {
+    outNew = morkBool_kTrue; // we created every array needed
+
+    // init mapScratch using slots from current map:
+    old->sMapScratch_Heap = sMap_Heap;
+    
+    old->sMapScratch_Slots = sMap_Slots;
+    old->sMapScratch_Keys = sMap_Keys;
+    old->sMapScratch_Vals = sMap_Vals;
+    
+    // replace all map array slots using the newly allocated members:
+    ++sMap_Seed; // the map has changed
+    sMap_Keys = newKeys;
+    sMap_Vals = newVals;
+    sMap_Slots = inSlots;
+  }
+  else // free any allocations if only partially successful
+  {
+    nsIMdbHeap* heap = sMap_Heap;
+    if ( newKeys )
+      heap->Free(ev->AsMdbEnv(), newKeys);
+    if ( newVals )
+      heap->Free(ev->AsMdbEnv(), newVals);
+    
+    MORK_MEMSET(old, 0, sizeof(morkMapScratch)); // zap scratch space
+  }
+  
+  return outNew;
+}
+
+void
+morkProbeMap::clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap)
+{
+  sProbeMap_Tag = 0;
+  sMap_Seed = 0;
+  sMap_Slots = 0;
+  sMap_Fill = 0;
+  sMap_Keys = 0;
+  sMap_Vals = 0;
+  sProbeMap_MaxFill = 0;
+  
+  sMap_Heap = ioMapHeap;
+  if ( !ioMapHeap )
+    ev->NilPointerError();
+}
+
+morkProbeMap::morkProbeMap(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioNodeHeap,
+  mork_size inKeySize, mork_size inValSize,
+  nsIMdbHeap* ioMapHeap, mork_size inSlots,
+  mork_bool inZeroIsClearKey)
+  
+: morkNode(ev, inUsage, ioNodeHeap)
+, sMap_Heap( ioMapHeap )
+    
+, sMap_Keys( 0 )
+, sMap_Vals( 0 )
+  
+, sMap_Seed( 0 )   // change count of members or structure
+    
+, sMap_Slots( 0 )  // count of slots in the hash table
+, sMap_Fill( 0 )   // number of used slots in the hash table
+
+, sMap_KeySize( 0 ) // size of each key (cannot be zero)
+, sMap_ValSize( 0 ) // size of each val (zero allowed)
+  
+, sMap_KeyIsIP( morkBool_kFalse ) // sMap_KeySize == sizeof(mork_ip)
+, sMap_ValIsIP( morkBool_kFalse ) // sMap_ValSize == sizeof(mork_ip)
+
+, sProbeMap_MaxFill( 0 )
+, sProbeMap_LazyClearOnAdd( 0 )
+, sProbeMap_ZeroIsClearKey( inZeroIsClearKey )
+, sProbeMap_Tag( 0 )
+{
+  // Note we cannot successfully call virtual ProbeMapClearKey() when we
+  // call init_probe_map() inside the constructor; so we leave this problem
+  // to the caller.  (The constructor will call ProbeMapClearKey() later
+  // after setting a suitable lazy flag to show this action is pending.)
+
+  if ( ev->Good() )
+  {
+    this->clear_probe_map(ev, ioMapHeap);
+    if ( ev->Good() )
+    {      
+      sMap_KeySize = inKeySize;
+      sMap_ValSize = inValSize;
+      sMap_KeyIsIP = ( inKeySize == sizeof(mork_ip) );
+      sMap_ValIsIP = ( inValSize == sizeof(mork_ip) );
+      
+      this->init_probe_map(ev, inSlots);
+      if ( ev->Good() )
+      {
+        if ( !inZeroIsClearKey ) // must lazy clear later with virtual method?
+          sProbeMap_LazyClearOnAdd = morkProbeMap_kLazyClearOnAdd;
+          
+        mNode_Derived = morkDerived_kProbeMap;
+      }
+    }
+  }
+}
+
+/*============================================================================*/
+
+/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+morkProbeMap::MapTest(morkEnv* ev,
+  const void* inMapKey, const void* inAppKey) const
+  // Note inMapKey is always a key already stored in the map, while inAppKey
+  //   is always a method argument parameter from a client method call.
+  //   This matters the most in morkProbeMap subclasses, which have the
+  //   responsibility of putting 'app' keys into slots for 'map' keys, and
+  //   the bit pattern representation might be different in such cases.
+  // morkTest_kHit means that inMapKey equals inAppKey (and this had better
+  //   also imply that hash(inMapKey) == hash(inAppKey)).
+  // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this
+  //   implies nothing at all about hash(inMapKey) and hash(inAppKey)).
+  // morkTest_kVoid means that inMapKey is not a valid key bit pattern,
+  //   which means that key slot in the map is not being used.  Note that
+  //   kVoid is only expected as a return value in morkProbeMap subclasses,
+  //   because morkProbeMap must ask whether a key slot is used or not. 
+  //   morkChainMap however, always knows when a key slot is used, so only
+  //   key slots expected to have valid bit patterns will be presented to
+  //   the MapTest() methods for morkChainMap subclasses.
+  //
+  // NOTE: it is very important that subclasses correctly return the value
+  // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern
+  // that means the slot is not being used, because this is the only way a
+  // probe map can terminate an unsuccessful search for a key in the map.
+{
+  mork_size keySize = sMap_KeySize;
+  if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP )
+  {
+    mork_ip mapKey = *((const mork_ip*) inMapKey);
+    if ( mapKey == *((const mork_ip*) inAppKey) )
+      return morkTest_kHit;
+    else
+    {
+      return ( mapKey )? morkTest_kMiss : morkTest_kVoid;
+    }
+  }
+  else
+  {
+    mork_bool allSame = morkBool_kTrue;
+    mork_bool allZero = morkBool_kTrue;
+    const mork_u1* ak = (const mork_u1*) inAppKey;
+    const mork_u1* mk = (const mork_u1*) inMapKey;
+    const mork_u1* end = mk + keySize;
+    --mk; // prepare for preincrement:
+    while ( ++mk < end )
+    {
+      mork_u1 byte = *mk;
+      if ( byte ) // any nonzero byte in map key means slot is not nil?
+        allZero = morkBool_kFalse;
+      if ( byte != *ak++ ) // bytes differ in map and app keys?
+        allSame = morkBool_kFalse;
+    }
+    if ( allSame )
+      return morkTest_kHit;
+    else
+      return ( allZero )? morkTest_kVoid : morkTest_kMiss;
+  }
+}
+
+/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+morkProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const
+{
+  mork_size keySize = sMap_KeySize;
+  if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP )
+  {
+    return *((const mork_ip*) inAppKey);
+  }
+  else
+  {
+    const mork_u1* key = (const mork_u1*) inAppKey;
+    const mork_u1* end = key + keySize;
+    --key; // prepare for preincrement:
+    while ( ++key < end )
+    {
+      if ( *key ) // any nonzero byte in map key means slot is not nil?
+        return morkBool_kFalse;
+    }
+    return morkBool_kTrue;
+  }
+  return (mork_u4) NS_PTR_TO_INT32(inAppKey);
+}
+
+
+/*============================================================================*/
+
+/*virtual*/ mork_u4
+morkProbeMap::ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const
+  // ProbeMapHashMapKey() does logically the same thing as MapHash(), and
+  // the default implementation actually calls virtual MapHash().  However,
+  // Subclasses must override this method whenever the formats of keys in
+  // the map differ from app keys outside the map, because MapHash() only
+  // works on keys in 'app' format, while ProbeMapHashMapKey() only works
+  // on keys in 'map' format.  This method is called in order to rehash all
+  // map keys when a map is grown, and this causes all old map members to
+  // move into new slot locations.
+  //
+  // Note it is absolutely imperative that a hash for a key in 'map' format
+  // be exactly the same the hash of the same key in 'app' format, or else
+  // maps will seem corrupt later when keys in 'app' format cannot be found.
+{
+  return this->MapHash(ev, inMapKey);
+}
+
+/*virtual*/ mork_bool
+morkProbeMap::ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey)
+  // ProbeMapIsKeyNil() must say whether the representation of logical 'nil'
+  // is currently found inside the key at ioMapKey, for a key found within
+  // the map.  The the map iterator uses this method to find map keys that
+  // are actually being used for valid map associations; otherwise the
+  // iterator cannot determine which map slots actually denote used keys.
+  // The default method version returns true if all the bits equal zero.
+{
+  if ( sMap_KeySize == sizeof(mork_ip) && sMap_KeyIsIP )
+  {
+    return !*((const mork_ip*) ioMapKey);
+  }
+  else
+  {
+    const mork_u1* key = (const mork_u1*) ioMapKey;
+    const mork_u1* end = key + sMap_KeySize;
+    --key; // prepare for preincrement:
+    while ( ++key < end )
+    {
+      if ( *key ) // any nonzero byte in map key means slot is not nil?
+        return morkBool_kFalse;
+    }
+    return morkBool_kTrue;
+  }
+}
+
+/*virtual*/ void
+morkProbeMap::ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+  void* ioMapKey, mork_count inKeyCount) // array of keys inside map
+  // ProbeMapClearKey() must put some representation of logical 'nil' into
+  // every key slot in the map, such that MapTest() will later recognize
+  // that this bit pattern shows each key slot is not actually being used.
+  //
+  // This method is typically called whenever the map is either created or
+  // grown into a larger size, where ioMapKey is a pointer to an array of
+  // inKeyCount keys, where each key is this->MapKeySize() bytes in size.
+  // Note that keys are assumed immediately adjacent with no padding, so
+  // if any alignment requirements must be met, then subclasses should have
+  // already accounted for this when specifying a key size in the map.
+  //
+  // Since this method will be called when a map is being grown in size,
+  // nothing should be assumed about the state slots of the map, since the
+  // ioMapKey array might not yet live in sMap_Keys, and the array length
+  // inKeyCount might not yet live in sMap_Slots.  However, the value kept
+  // in sMap_KeySize never changes, so this->MapKeySize() is always correct.
+{
+  if ( ioMapKey && inKeyCount )
+  {
+    MORK_MEMSET(ioMapKey, 0, (inKeyCount * sMap_KeySize));
+  }
+  else
+    ev->NilPointerWarning();
+}
+
+/*virtual*/ void
+morkProbeMap::ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+  const void* inAppKey, const void* inAppVal, // (key,val) outside map
+  void* outMapKey, void* outMapVal)      // (key,val) inside map
+  // This method actually puts keys and vals in the map in suitable format.
+  //
+  // ProbeMapPushIn() must copy a caller key and value in 'app' format
+  // into the map slots provided, which are in 'map' format.  When the
+  // 'app' and 'map' formats are identical, then this is just a bitwise
+  // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes,
+  // and this is exactly what the default implementation performs.  However,
+  // if 'app' and 'map' formats are different, and MapTest() depends on this
+  // difference in format, then subclasses must override this method to do
+  // whatever is necessary to store the input app key in output map format.
+  //
+  // Do NOT write more than this->MapKeySize() bytes of a map key, or more
+  // than this->MapValSize() bytes of a map val, or corruption might ensue.
+  //
+  // The inAppKey and inAppVal parameters are the same ones passed into a
+  // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones
+  // determined by how the map currently positions key inAppKey in the map.
+  //
+  // Note any key or val parameter can be a null pointer, in which case
+  // this method must do nothing with those parameters.  In particular, do
+  // no key move at all when either inAppKey or outMapKey is nil, and do
+  // no val move at all when either inAppVal or outMapVal is nil.  Note that
+  // outMapVal should always be nil when this->MapValSize() is nil.
+{
+}
+
+/*virtual*/ void
+morkProbeMap::ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+  const void* inMapKey, const void* inMapVal, // (key,val) inside map
+  void* outAppKey, void* outAppVal) const    // (key,val) outside map
+  // This method actually gets keys and vals from the map in suitable format.
+  //
+  // ProbeMapPullOut() must copy a key and val in 'map' format into the
+  // caller key and val slots provided, which are in 'app' format.  When the
+  // 'app' and 'map' formats are identical, then this is just a bitwise
+  // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes,
+  // and this is exactly what the default implementation performs.  However,
+  // if 'app' and 'map' formats are different, and MapTest() depends on this
+  // difference in format, then subclasses must override this method to do
+  // whatever is necessary to store the input map key in output app format.
+  //
+  // The outAppKey and outAppVal parameters are the same ones passed into a
+  // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are
+  // determined by how the map currently positions the target key in the map.
+  //
+  // Note any key or val parameter can be a null pointer, in which case
+  // this method must do nothing with those parameters.  In particular, do
+  // no key move at all when either inMapKey or outAppKey is nil, and do
+  // no val move at all when either inMapVal or outAppVal is nil.  Note that
+  // inMapVal should always be nil when this->MapValSize() is nil.
+{
+}
+
+
+/*============================================================================*/
+/* morkProbeMapIter */
+
+morkProbeMapIter::morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap)
+: sProbeMapIter_Map( 0 )
+, sProbeMapIter_Seed( 0 )
+, sProbeMapIter_HereIx( morkProbeMapIter_kBeforeIx )
+{
+  if ( ioMap )
+  {
+    if ( ioMap->GoodProbeMap() )
+    {
+      if ( ioMap->need_lazy_init() ) // pending lazy action?
+        ioMap->probe_map_lazy_init(ev);
+        
+      sProbeMapIter_Map = ioMap;
+      sProbeMapIter_Seed = ioMap->sMap_Seed;
+    }
+    else ioMap->ProbeMapBadTagError(ev);
+  }
+  else ev->NilPointerError();
+}
+
+void morkProbeMapIter::CloseMapIter(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+  sProbeMapIter_Map = 0;
+  sProbeMapIter_Seed = 0;
+
+  sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx;
+}
+
+morkProbeMapIter::morkProbeMapIter( )
+// zero most slots; caller must call InitProbeMapIter()
+{
+  sProbeMapIter_Map = 0;
+  sProbeMapIter_Seed = 0;
+
+  sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx;
+}
+
+void morkProbeMapIter::InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap)
+{
+  sProbeMapIter_Map = 0;
+  sProbeMapIter_Seed = 0;
+
+  sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx;
+
+  if ( ioMap )
+  {
+    if ( ioMap->GoodProbeMap() )
+    {
+      if ( ioMap->need_lazy_init() ) // pending lazy action?
+        ioMap->probe_map_lazy_init(ev);
+        
+      sProbeMapIter_Map = ioMap;
+      sProbeMapIter_Seed = ioMap->sMap_Seed;
+    }
+    else ioMap->ProbeMapBadTagError(ev);
+  }
+  else ev->NilPointerError();
+}
+ 
+mork_bool morkProbeMapIter::IterFirst(morkEnv* ev,
+  void* outAppKey, void* outAppVal)
+{
+  sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done
+  morkProbeMap* map = sProbeMapIter_Map;
+  
+  if ( map && map->GoodProbeMap() ) /* looks good? */
+  {
+    sProbeMapIter_Seed = map->sMap_Seed; /* sync the seeds */
+    
+    mork_u1* k = map->sMap_Keys;  // array of keys, each of size sMap_KeySize
+    mork_num size = map->sMap_KeySize;  // number of bytes in each key
+    mork_count slots = map->sMap_Slots; // total number of key buckets
+    mork_pos here = 0;  // first hash bucket
+    
+    while ( here < (mork_pos)slots )
+    {
+      if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) )
+      {
+        map->get_probe_kv(ev, outAppKey, outAppVal, here);
+        
+        sProbeMapIter_HereIx = (mork_i4) here;
+        return morkBool_kTrue;
+      }
+      ++here; // next bucket
+    } 
+  }
+  else map->ProbeMapBadTagError(ev);
+
+  return morkBool_kFalse;
+}
+
+mork_bool morkProbeMapIter::IterNext(morkEnv* ev,
+  void* outAppKey, void* outAppVal)
+{
+  morkProbeMap* map = sProbeMapIter_Map;
+  
+  if ( map && map->GoodProbeMap() ) /* looks good? */
+  {    
+    if ( sProbeMapIter_Seed == map->sMap_Seed ) /* in sync? */
+    {
+      if ( sProbeMapIter_HereIx != morkProbeMapIter_kAfterIx )
+      {
+        mork_pos here = (mork_pos) sProbeMapIter_HereIx;
+        if ( sProbeMapIter_HereIx < 0 )
+          here = 0;
+        else
+          ++here;
+          
+        sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done
+
+        mork_u1* k = map->sMap_Keys;  // key array, each of size sMap_KeySize
+        mork_num size = map->sMap_KeySize;  // number of bytes in each key
+        mork_count slots = map->sMap_Slots; // total number of key buckets
+        
+        while ( here < (mork_pos)slots )
+        {
+          if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) )
+          {
+            map->get_probe_kv(ev, outAppKey, outAppVal, here);
+            
+            sProbeMapIter_HereIx = (mork_i4) here;
+            return morkBool_kTrue;
+          }
+          ++here; // next bucket
+        } 
+      }
+    }
+    else map->MapSeedOutOfSyncError(ev);
+  }
+  else map->ProbeMapBadTagError(ev);
+
+  return morkBool_kFalse;
+}
+
+mork_bool morkProbeMapIter::IterHere(morkEnv* ev,
+  void* outAppKey, void* outAppVal)
+{
+  morkProbeMap* map = sProbeMapIter_Map;
+  
+  if ( map && map->GoodProbeMap() ) /* looks good? */
+  {    
+    if ( sProbeMapIter_Seed == map->sMap_Seed ) /* in sync? */
+    {
+      mork_pos here = (mork_pos) sProbeMapIter_HereIx;
+      mork_count slots = map->sMap_Slots; // total number of key buckets
+      if ( sProbeMapIter_HereIx >= 0 && (here < (mork_pos)slots))
+      {
+        mork_u1* k = map->sMap_Keys;  // key array, each of size sMap_KeySize
+        mork_num size = map->sMap_KeySize;  // number of bytes in each key
+
+        if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) )
+        {
+          map->get_probe_kv(ev, outAppKey, outAppVal, here);
+          return morkBool_kTrue;
+        }
+      }
+    }
+    else map->MapSeedOutOfSyncError(ev);
+  }
+  else map->ProbeMapBadTagError(ev);
+
+  return morkBool_kFalse;
+}
+
+mork_change*
+morkProbeMapIter::First(morkEnv* ev, void* outKey, void* outVal)
+{
+  if ( this->IterFirst(ev, outKey, outVal) )
+    return &sProbeMapIter_Change;
+  
+  return (mork_change*) 0;
+}
+
+mork_change*
+morkProbeMapIter::Next(morkEnv* ev, void* outKey, void* outVal)
+{
+  if ( this->IterNext(ev, outKey, outVal) )
+    return &sProbeMapIter_Change;
+  
+  return (mork_change*) 0;
+}
+
+mork_change*
+morkProbeMapIter::Here(morkEnv* ev, void* outKey, void* outVal)
+{
+  if ( this->IterHere(ev, outKey, outVal) )
+    return &sProbeMapIter_Change;
+  
+  return (mork_change*) 0;
+}
+
+mork_change*
+morkProbeMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal)
+{
+  morkProbeMap::ProbeMapCutError(ev);
+  
+  return (mork_change*) 0;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// NOTE: the following methods ONLY work for sMap_ValIsIP pointer values.
+// (Note the implied assumption that zero is never a good value pattern.)
+
+void* morkProbeMapIter::IterFirstVal(morkEnv* ev, void* outKey)
+// equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; }
+{
+  morkProbeMap* map = sProbeMapIter_Map;
+  if ( map )
+  {
+    if ( map->sMap_ValIsIP )
+    {
+      void* v = 0;
+      this->IterFirst(ev, outKey, &v);
+      return v;
+    }
+    else
+      map->MapValIsNotIPError(ev);
+  }
+  return (void*) 0;
+}
+
+void* morkProbeMapIter::IterNextVal(morkEnv* ev, void* outKey)
+// equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; }
+{
+  morkProbeMap* map = sProbeMapIter_Map;
+  if ( map )
+  {
+    if ( map->sMap_ValIsIP )
+    {
+      void* v = 0;
+      this->IterNext(ev, outKey, &v);
+      return v;
+    }
+    else
+      map->MapValIsNotIPError(ev);
+  }
+  return (void*) 0;
+}
+
+void* morkProbeMapIter::IterHereVal(morkEnv* ev, void* outKey)
+// equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; }
+{
+  morkProbeMap* map = sProbeMapIter_Map;
+  if ( map )
+  {
+    if ( map->sMap_ValIsIP )
+    {
+      void* v = 0;
+      this->IterHere(ev, outKey, &v);
+      return v;
+    }
+    else
+      map->MapValIsNotIPError(ev);
+  }
+  return (void*) 0;
+}
+
+// NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values.
+// (Note the implied assumption that zero is never a good key pattern.)
+
+void* morkProbeMapIter::IterFirstKey(morkEnv* ev)
+// equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; }
+{
+  morkProbeMap* map = sProbeMapIter_Map;
+  if ( map )
+  {
+    if ( map->sMap_KeyIsIP )
+    {
+      void* k = 0;
+      this->IterFirst(ev, &k, (void*) 0);
+      return k;
+    }
+    else
+      map->MapKeyIsNotIPError(ev);
+  }
+  return (void*) 0;
+}
+
+void* morkProbeMapIter::IterNextKey(morkEnv* ev)
+// equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; }
+{
+  morkProbeMap* map = sProbeMapIter_Map;
+  if ( map )
+  {
+    if ( map->sMap_KeyIsIP )
+    {
+      void* k = 0;
+      this->IterNext(ev, &k, (void*) 0);
+      return k;
+    }
+    else
+      map->MapKeyIsNotIPError(ev);
+  }
+  return (void*) 0;
+}
+
+void* morkProbeMapIter::IterHereKey(morkEnv* ev)
+// equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; }
+{
+  morkProbeMap* map = sProbeMapIter_Map;
+  if ( map )
+  {
+    if ( map->sMap_KeyIsIP )
+    {
+      void* k = 0;
+      this->IterHere(ev, &k, (void*) 0);
+      return k;
+    }
+    else
+      map->MapKeyIsNotIPError(ev);
+  }
+  return (void*) 0;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkProbeMap.h
@@ -0,0 +1,431 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This code is a port to NS Mork from public domain Mithril C++ sources.
+// Note many code comments here come verbatim from cut-and-pasted Mithril.
+// In many places, code is identical; Mithril versions stay public domain.
+// Changes in porting are mainly class type and scalar type name changes.
+
+#ifndef _MORKPROBEMAP_
+#define _MORKPROBEMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class morkMapScratch { // utility class used by map subclasses
+public:
+  nsIMdbHeap*  sMapScratch_Heap;     // cached sMap_Heap
+  mork_count   sMapScratch_Slots;    // cached sMap_Slots
+  
+  mork_u1*     sMapScratch_Keys;     // cached sMap_Keys
+  mork_u1*     sMapScratch_Vals;     // cached sMap_Vals
+  
+public:
+  void halt_map_scratch(morkEnv* ev);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kProbeMap   0x7072 /* ascii 'pr' */
+#define morkProbeMap_kTag     0x70724D50 /* ascii 'prMP' */
+
+#define morkProbeMap_kLazyClearOnAdd ((mork_u1) 'c')
+ 
+class morkProbeMap: public morkNode {
+
+protected:
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+protected:
+  // { begin morkMap slots
+  nsIMdbHeap* sMap_Heap; // strong ref to heap allocating all space
+    
+  mork_u1*    sMap_Keys;
+  mork_u1*    sMap_Vals;
+  
+  mork_count  sMap_Seed;   // change count of members or structure
+    
+  mork_count  sMap_Slots;  // count of slots in the hash table
+  mork_fill   sMap_Fill;   // number of used slots in the hash table
+
+  mork_size   sMap_KeySize; // size of each key (cannot be zero)
+  mork_size   sMap_ValSize; // size of each val (zero allowed)
+  
+  mork_bool   sMap_KeyIsIP;     // sMap_KeySize == sizeof(mork_ip)
+  mork_bool   sMap_ValIsIP;     // sMap_ValSize == sizeof(mork_ip)
+  mork_u1     sMap_Pad[ 2 ];    // for u4 alignment
+  // } end morkMap slots
+    
+  friend class morkProbeMapIter; // for access to protected slots
+
+public: // getters
+  mork_count  MapSeed() const { return sMap_Seed; }
+    
+  mork_count  MapSlots() const { return sMap_Slots; }
+  mork_fill   MapFill() const { return sMap_Fill; }
+
+  mork_size   MapKeySize() const { return sMap_KeySize; }
+  mork_size   MapValSize() const { return sMap_ValSize; }
+  
+  mork_bool   MapKeyIsIP() const { return sMap_KeyIsIP; }
+  mork_bool   MapValIsIP() const { return sMap_ValIsIP; }
+
+protected: // slots
+  // { begin morkProbeMap slots
+   
+  mork_fill   sProbeMap_MaxFill; // max sMap_Fill before map must grow
+  
+  mork_u1     sProbeMap_LazyClearOnAdd; // true if kLazyClearOnAdd
+  mork_bool   sProbeMap_ZeroIsClearKey; // zero is adequate to clear keys
+  mork_u1     sProbeMap_Pad[ 2 ]; // for u4 alignment
+  
+  mork_u4     sProbeMap_Tag; 
+ 
+  // } end morkProbeMap slots
+    
+public: // lazy clear on add
+
+  mork_bool need_lazy_init() const 
+  { return sProbeMap_LazyClearOnAdd == morkProbeMap_kLazyClearOnAdd; }
+
+public: // typing
+  mork_bool   GoodProbeMap() const
+  { return sProbeMap_Tag == morkProbeMap_kTag; }
+    
+protected: // utilities
+
+  void* clear_alloc(morkEnv* ev, mork_size inSize);
+
+  mork_u1*    map_new_vals(morkEnv* ev, mork_num inSlots);
+  mork_u1*    map_new_keys(morkEnv* ev, mork_num inSlots);
+
+  void clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap);
+  void init_probe_map(morkEnv* ev, mork_size inSlots);
+  void probe_map_lazy_init(morkEnv* ev);
+
+  mork_bool new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots);
+  
+  mork_test find_key_pos(morkEnv* ev, const void* inAppKey,
+    mork_u4 inHash, mork_pos* outPos) const;
+  
+  void put_probe_kv(morkEnv* ev,
+    const void* inAppKey, const void* inAppVal, mork_pos inPos);
+  void get_probe_kv(morkEnv* ev,
+    void* outAppKey, void* outAppVal, mork_pos inPos) const;
+    
+  mork_bool grow_probe_map(morkEnv* ev);
+  void      rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch);
+  void      revert_map(morkEnv* ev, morkMapScratch* ioScratch);
+
+public: // errors
+  void ProbeMapBadTagError(morkEnv* ev) const;
+  void WrapWithNoVoidSlotError(morkEnv* ev) const;
+  void GrowFailsMaxFillError(morkEnv* ev) const;
+  void MapKeyIsNotIPError(morkEnv* ev) const;
+  void MapValIsNotIPError(morkEnv* ev) const;
+
+  void MapNilKeysError(morkEnv* ev);
+  void MapZeroKeySizeError(morkEnv* ev);
+
+  void MapSeedOutOfSyncError(morkEnv* ev);
+  void MapFillUnderflowWarning(morkEnv* ev);
+
+  static void ProbeMapCutError(morkEnv* ev);
+
+  // { ===== begin morkMap methods =====
+public:
+
+  virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+  MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const;
+    // Note inMapKey is always a key already stored in the map, while inAppKey
+    //   is always a method argument parameter from a client method call.
+    //   This matters the most in morkProbeMap subclasses, which have the
+    //   responsibility of putting 'app' keys into slots for 'map' keys, and
+    //   the bit pattern representation might be different in such cases.
+    // morkTest_kHit means that inMapKey equals inAppKey (and this had better
+    //   also imply that hash(inMapKey) == hash(inAppKey)).
+    // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this
+    //   implies nothing at all about hash(inMapKey) and hash(inAppKey)).
+    // morkTest_kVoid means that inMapKey is not a valid key bit pattern,
+    //   which means that key slot in the map is not being used.  Note that
+    //   kVoid is only expected as a return value in morkProbeMap subclasses,
+    //   because morkProbeMap must ask whether a key slot is used or not. 
+    //   morkChainMap however, always knows when a key slot is used, so only
+    //   key slots expected to have valid bit patterns will be presented to
+    //   the MapTest() methods for morkChainMap subclasses.
+    //
+    // NOTE: it is very important that subclasses correctly return the value
+    // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern
+    // that means the slot is not being used, because this is the only way a
+    // probe map can terminate an unsuccessful search for a key in the map.
+
+  virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+  MapHash(morkEnv* ev, const void* inAppKey) const;
+
+  virtual mork_bool
+  MapAtPut(morkEnv* ev, const void* inAppKey, const void* inAppVal,
+    void* outAppKey, void* outAppVal);
+    
+  virtual mork_bool
+  MapAt(morkEnv* ev, const void* inAppKey, void* outAppKey, void* outAppVal);
+    
+  virtual mork_num
+  MapCutAll(morkEnv* ev);
+  // } ===== end morkMap methods =====
+
+    
+  // { ===== begin morkProbeMap methods =====
+public:
+
+  virtual mork_u4
+  ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const;
+    // ProbeMapHashMapKey() does logically the same thing as MapHash(), and
+    // the default implementation actually calls virtual MapHash().  However,
+    // Subclasses must override this method whenever the formats of keys in
+    // the map differ from app keys outside the map, because MapHash() only
+    // works on keys in 'app' format, while ProbeMapHashMapKey() only works
+    // on keys in 'map' format.  This method is called in order to rehash all
+    // map keys when a map is grown, and this causes all old map members to
+    // move into new slot locations.
+    //
+    // Note it is absolutely imperative that a hash for a key in 'map' format
+    // be exactly the same the hash of the same key in 'app' format, or else
+    // maps will seem corrupt later when keys in 'app' format cannot be found.
+
+  virtual mork_bool
+  ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+    // ProbeMapIsKeyNil() must say whether the representation of logical 'nil'
+    // is currently found inside the key at ioMapKey, for a key found within
+    // the map.  The the map iterator uses this method to find map keys that
+    // are actually being used for valid map associations; otherwise the
+    // iterator cannot determine which map slots actually denote used keys.
+    // The default method version returns true if all the bits equal zero.
+
+  virtual void
+  ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+    void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+    // ProbeMapClearKey() must put some representation of logical 'nil' into
+    // every key slot in the map, such that MapTest() will later recognize
+    // that this bit pattern shows each key slot is not actually being used.
+    //
+    // This method is typically called whenever the map is either created or
+    // grown into a larger size, where ioMapKey is a pointer to an array of
+    // inKeyCount keys, where each key is this->MapKeySize() bytes in size.
+    // Note that keys are assumed immediately adjacent with no padding, so
+    // if any alignment requirements must be met, then subclasses should have
+    // already accounted for this when specifying a key size in the map.
+    //
+    // Since this method will be called when a map is being grown in size,
+    // nothing should be assumed about the state slots of the map, since the
+    // ioMapKey array might not yet live in sMap_Keys, and the array length
+    // inKeyCount might not yet live in sMap_Slots.  However, the value kept
+    // in sMap_KeySize never changes, so this->MapKeySize() is always correct.
+
+  virtual void
+  ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+    const void* inAppKey, const void* inAppVal, // (key,val) outside map
+    void* outMapKey, void* outMapVal);      // (key,val) inside map
+    // This method actually puts keys and vals in the map in suitable format.
+    //
+    // ProbeMapPushIn() must copy a caller key and value in 'app' format
+    // into the map slots provided, which are in 'map' format.  When the
+    // 'app' and 'map' formats are identical, then this is just a bitwise
+    // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes,
+    // and this is exactly what the default implementation performs.  However,
+    // if 'app' and 'map' formats are different, and MapTest() depends on this
+    // difference in format, then subclasses must override this method to do
+    // whatever is necessary to store the input app key in output map format.
+    //
+    // Do NOT write more than this->MapKeySize() bytes of a map key, or more
+    // than this->MapValSize() bytes of a map val, or corruption might ensue.
+    //
+    // The inAppKey and inAppVal parameters are the same ones passed into a
+    // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones
+    // determined by how the map currently positions key inAppKey in the map.
+    //
+    // Note any key or val parameter can be a null pointer, in which case
+    // this method must do nothing with those parameters.  In particular, do
+    // no key move at all when either inAppKey or outMapKey is nil, and do
+    // no val move at all when either inAppVal or outMapVal is nil.  Note that
+    // outMapVal should always be nil when this->MapValSize() is nil.
+
+  virtual void
+  ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+    const void* inMapKey, const void* inMapVal, // (key,val) inside map
+    void* outAppKey, void* outAppVal) const;    // (key,val) outside map
+    // This method actually gets keys and vals from the map in suitable format.
+    //
+    // ProbeMapPullOut() must copy a key and val in 'map' format into the
+    // caller key and val slots provided, which are in 'app' format.  When the
+    // 'app' and 'map' formats are identical, then this is just a bitwise
+    // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes,
+    // and this is exactly what the default implementation performs.  However,
+    // if 'app' and 'map' formats are different, and MapTest() depends on this
+    // difference in format, then subclasses must override this method to do
+    // whatever is necessary to store the input map key in output app format.
+    //
+    // The outAppKey and outAppVal parameters are the same ones passed into a
+    // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are
+    // determined by how the map currently positions the target key in the map.
+    //
+    // Note any key or val parameter can be a null pointer, in which case
+    // this method must do nothing with those parameters.  In particular, do
+    // no key move at all when either inMapKey or outAppKey is nil, and do
+    // no val move at all when either inMapVal or outAppVal is nil.  Note that
+    // inMapVal should always be nil when this->MapValSize() is nil.
+  
+  // } ===== end morkProbeMap methods =====
+    
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseProbeMap() only if open
+  virtual ~morkProbeMap(); // assert that CloseProbeMap() executed earlier
+  
+public: // morkProbeMap construction & destruction
+  morkProbeMap(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioNodeHeap,
+  mork_size inKeySize, mork_size inValSize,
+  nsIMdbHeap* ioMapHeap, mork_size inSlots,
+  mork_bool inZeroIsClearKey);
+  
+  void CloseProbeMap(morkEnv* ev); // called by 
+  
+public: // dynamic type identification
+  mork_bool IsProbeMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kProbeMap; }
+// } ===== end morkNode methods =====
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakMap(morkMap* me,
+    morkEnv* ev, morkMap** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongMap(morkMap* me,
+    morkEnv* ev, morkMap** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+/*============================================================================*/
+/* morkProbeMapIter */
+
+#define morkProbeMapIter_kBeforeIx ((mork_i4) -1) /* before first member */
+#define morkProbeMapIter_kAfterIx  ((mork_i4) -2) /* after last member */
+
+class morkProbeMapIter {
+
+protected:
+  morkProbeMap* sProbeMapIter_Map;      // nonref
+  mork_num      sProbeMapIter_Seed;     // iter's cached copy of map's seed
+  
+  mork_i4       sProbeMapIter_HereIx;
+  
+  mork_change   sProbeMapIter_Change;   // morkMapIter API simulation dummy
+  mork_u1       sProbeMapIter_Pad[ 3 ]; // for u4 alignment
+  
+public:
+  morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap);
+  void CloseMapIter(morkEnv* ev);
+ 
+  morkProbeMapIter( ); // zero most slots; caller must call InitProbeMapIter()
+
+protected: // protected so subclasses must provide suitable typesafe inlines:
+
+  void InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap);
+   
+  void InitMapIter(morkEnv* ev, morkProbeMap* ioMap) // morkMapIter compatibility
+  { this->InitProbeMapIter(ev, ioMap); }
+   
+  mork_bool IterFirst(morkEnv* ev, void* outKey, void* outVal);
+  mork_bool IterNext(morkEnv* ev, void* outKey, void* outVal);
+  mork_bool IterHere(morkEnv* ev, void* outKey, void* outVal);
+   
+  // NOTE: the following methods ONLY work for sMap_ValIsIP pointer values.
+  // (Note the implied assumption that zero is never a good value pattern.)
+  
+  void*     IterFirstVal(morkEnv* ev, void* outKey);
+  // equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; }
+  
+  void*     IterNextVal(morkEnv* ev, void* outKey);
+  // equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; }
+
+  void*     IterHereVal(morkEnv* ev, void* outKey);
+  // equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; }
+
+  // NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values.
+  // (Note the implied assumption that zero is never a good key pattern.)
+  
+  void*     IterFirstKey(morkEnv* ev);
+  // equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; }
+  
+  void*     IterNextKey(morkEnv* ev);
+  // equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; }
+
+  void*     IterHereKey(morkEnv* ev);
+  // equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; }
+
+public: // simulation of the morkMapIter API for morkMap compatibility:  
+  mork_change* First(morkEnv* ev, void* outKey, void* outVal);
+  mork_change* Next(morkEnv* ev, void* outKey, void* outVal);
+  mork_change* Here(morkEnv* ev, void* outKey, void* outVal);
+  
+  mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKPROBEMAP_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkQuickSort.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/*-
+ * Copyright (c) 1992, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* We need this because Solaris' version of qsort is broken and
+ * causes array bounds reads.
+ */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKQUICKSORT_
+#include "morkQuickSort.h"
+#endif
+
+#if !defined(DEBUG) && (defined(__cplusplus) || defined(__gcc))
+# ifndef INLINE
+#  define INLINE inline
+# endif
+#else
+# define INLINE
+#endif
+
+static INLINE mork_u1*
+morkQS_med3(mork_u1 *, mork_u1 *, mork_u1 *, mdbAny_Order, void *);
+
+static INLINE void
+morkQS_swapfunc(mork_u1 *, mork_u1 *, int, int);
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define morkQS_swapcode(TYPE, parmi, parmj, n) {     \
+  long i = (n) / sizeof (TYPE);       \
+  register TYPE *pi = (TYPE *) (parmi);     \
+  register TYPE *pj = (TYPE *) (parmj);     \
+  do {             \
+    register TYPE  t = *pi;    \
+    *pi++ = *pj;        \
+    *pj++ = t;        \
+        } while (--i > 0);        \
+}
+
+#define morkQS_SwapInit(a, es) swaptype = (a - (mork_u1 *)0) % sizeof(long) || \
+  es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static INLINE void
+morkQS_swapfunc(mork_u1* a, mork_u1* b, int n, int swaptype)
+{
+  if(swaptype <= 1)
+    morkQS_swapcode(long, a, b, n)
+  else
+    morkQS_swapcode(mork_u1, a, b, n)
+}
+
+#define morkQS_swap(a, b)          \
+  if (swaptype == 0) {        \
+    long t = *(long *)(a);      \
+    *(long *)(a) = *(long *)(b);    \
+    *(long *)(b) = t;      \
+  } else            \
+    morkQS_swapfunc(a, b, (int)inSize, swaptype)
+
+#define morkQS_vecswap(a, b, n)   if ((n) > 0) morkQS_swapfunc(a, b, (int)n, swaptype)
+
+static INLINE mork_u1 *
+morkQS_med3(mork_u1* a, mork_u1* b, mork_u1* c, mdbAny_Order cmp, void* closure)
+{
+  return (*cmp)(a, b, closure) < 0 ?
+    ((*cmp)(b, c, closure) < 0 ? b : ((*cmp)(a, c, closure) < 0 ? c : a ))
+      :((*cmp)(b, c, closure) > 0 ? b : ((*cmp)(a, c, closure) < 0 ? a : c ));
+}
+
+#define morkQS_MIN(x,y)     ((x)<(y)?(x):(y))
+
+void
+morkQuickSort(mork_u1* ioVec, mork_u4 inCount, mork_u4 inSize,
+  mdbAny_Order inOrder, void* ioClosure)
+{
+  mork_u1* pa, *pb, *pc, *pd, *pl, *pm, *pn;
+  int d, r, swaptype, swap_cnt;
+
+tailCall:  morkQS_SwapInit(ioVec, inSize);
+  swap_cnt = 0;
+  if (inCount < 7) {
+    for (pm = ioVec + inSize; pm < ioVec + inCount * inSize; pm += inSize)
+      for (pl = pm; pl > ioVec && (*inOrder)(pl - inSize, pl, ioClosure) > 0;
+           pl -= inSize)
+        morkQS_swap(pl, pl - inSize);
+    return;
+  }
+  pm = ioVec + (inCount / 2) * inSize;
+  if (inCount > 7) {
+    pl = ioVec;
+    pn = ioVec + (inCount - 1) * inSize;
+    if (inCount > 40) {
+      d = (inCount / 8) * inSize;
+      pl = morkQS_med3(pl, pl + d, pl + 2 * d, inOrder, ioClosure);
+      pm = morkQS_med3(pm - d, pm, pm + d, inOrder, ioClosure);
+      pn = morkQS_med3(pn - 2 * d, pn - d, pn, inOrder, ioClosure);
+    }
+    pm = morkQS_med3(pl, pm, pn, inOrder, ioClosure);
+  }
+  morkQS_swap(ioVec, pm);
+  pa = pb = ioVec + inSize;
+
+  pc = pd = ioVec + (inCount - 1) * inSize;
+  for (;;) {
+    while (pb <= pc && (r = (*inOrder)(pb, ioVec, ioClosure)) <= 0) {
+      if (r == 0) {
+        swap_cnt = 1;
+        morkQS_swap(pa, pb);
+        pa += inSize;
+      }
+      pb += inSize;
+    }
+    while (pb <= pc && (r = (*inOrder)(pc, ioVec, ioClosure)) >= 0) {
+      if (r == 0) {
+        swap_cnt = 1;
+        morkQS_swap(pc, pd);
+        pd -= inSize;
+      }
+      pc -= inSize;
+    }
+    if (pb > pc)
+      break;
+    morkQS_swap(pb, pc);
+    swap_cnt = 1;
+    pb += inSize;
+    pc -= inSize;
+  }
+  if (swap_cnt == 0) {  /* Switch to insertion sort */
+    for (pm = ioVec + inSize; pm < ioVec + inCount * inSize; pm += inSize)
+      for (pl = pm; pl > ioVec && (*inOrder)(pl - inSize, pl, ioClosure) > 0;
+           pl -= inSize)
+        morkQS_swap(pl, pl - inSize);
+    return;
+  }
+
+  pn = ioVec + inCount * inSize;
+  r = morkQS_MIN(pa - ioVec, pb - pa);
+  morkQS_vecswap(ioVec, pb - r, r);
+  r = morkQS_MIN(pd - pc, (int)(pn - pd - inSize));
+  morkQS_vecswap(pb, pn - r, r);
+  if ((r = pb - pa) > (int)inSize)
+        morkQuickSort(ioVec, r / inSize, inSize, inOrder, ioClosure);
+  if ((r = pd - pc) > (int)inSize) {
+    /* Iterate rather than recurse to save stack space */
+    ioVec = pn - r;
+    inCount = r / inSize;
+    goto tailCall;
+  }
+/*    morkQuickSort(pn - r, r / inSize, inSize, inOrder, ioClosure);*/
+}
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkQuickSort.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKQUICKSORT_
+#define _MORKQUICKSORT_ 1
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+extern void
+morkQuickSort(mork_u1* ioVec, mork_u4 inCount, mork_u4 inSize,
+  mdbAny_Order inOrder, void* ioClosure);
+ 
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKQUICKSORT_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRow.cpp
@@ -0,0 +1,965 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+#ifndef _MORKCELLOBJECT_
+#include "morkCellObject.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKROWCELLCURSOR_
+#include "morkRowCellCursor.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+// notifications regarding row changes:
+
+void morkRow::NoteRowAddCol(morkEnv* ev, mork_column inColumn)
+{
+  if ( !this->IsRowRewrite() )
+  {
+    mork_delta newDelta;
+    morkDelta_Init(newDelta, inColumn, morkChange_kAdd);
+    
+    if ( newDelta != mRow_Delta ) // not repeating existing data?
+    {
+      if ( this->HasRowDelta() ) // already have one change recorded?
+        this->SetRowRewrite(); // just plan to write all row cells
+      else
+        this->SetRowDelta(inColumn, morkChange_kAdd);
+    }
+  }
+  else
+    this->ClearRowDelta();
+}
+
+void morkRow::NoteRowCutCol(morkEnv* ev, mork_column inColumn)
+{
+  if ( !this->IsRowRewrite() )
+  {
+    mork_delta newDelta;
+    morkDelta_Init(newDelta, inColumn, morkChange_kCut);
+    
+    if ( newDelta != mRow_Delta ) // not repeating existing data?
+    {
+      if ( this->HasRowDelta() ) // already have one change recorded?
+        this->SetRowRewrite(); // just plan to write all row cells
+      else
+        this->SetRowDelta(inColumn, morkChange_kCut);
+    }
+  }
+  else
+    this->ClearRowDelta();
+}
+
+void morkRow::NoteRowSetCol(morkEnv* ev, mork_column inColumn)
+{
+  if ( !this->IsRowRewrite() )
+  {
+    if ( this->HasRowDelta() ) // already have one change recorded?
+      this->SetRowRewrite(); // just plan to write all row cells
+    else
+      this->SetRowDelta(inColumn, morkChange_kSet);
+  }
+  else
+    this->ClearRowDelta();
+}
+
+void morkRow::NoteRowSetAll(morkEnv* ev)
+{
+  this->SetRowRewrite(); // just plan to write all row cells
+  this->ClearRowDelta();
+}
+
+mork_u2
+morkRow::AddRowGcUse(morkEnv* ev)
+{
+  if ( this->IsRow() )
+  {
+    if ( mRow_GcUses < morkRow_kMaxGcUses ) // not already maxed out?
+      ++mRow_GcUses;
+  }
+  else
+    this->NonRowTypeError(ev);
+    
+  return mRow_GcUses;
+}
+
+mork_u2
+morkRow::CutRowGcUse(morkEnv* ev)
+{
+  if ( this->IsRow() )
+  {
+    if ( mRow_GcUses ) // any outstanding uses to cut?
+    {
+      if ( mRow_GcUses < morkRow_kMaxGcUses ) // not frozen at max?
+        --mRow_GcUses;
+    }
+    else
+      this->GcUsesUnderflowWarning(ev);
+  }
+  else
+    this->NonRowTypeError(ev);
+    
+  return mRow_GcUses;
+}
+
+/*static*/ void
+morkRow::GcUsesUnderflowWarning(morkEnv* ev)
+{
+  ev->NewWarning("mRow_GcUses underflow");
+}
+
+
+/*static*/ void
+morkRow::NonRowTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkRow");
+}
+
+/*static*/ void
+morkRow::NonRowTypeWarning(morkEnv* ev)
+{
+  ev->NewWarning("non morkRow");
+}
+
+/*static*/ void
+morkRow::LengthBeyondMaxError(morkEnv* ev)
+{
+  ev->NewError("mRow_Length over max");
+}
+
+/*static*/ void
+morkRow::ZeroColumnError(morkEnv* ev)
+{
+  ev->NewError(" zero mork_column");
+}
+
+/*static*/ void
+morkRow::NilCellsError(morkEnv* ev)
+{
+  ev->NewError("nil mRow_Cells");
+}
+
+void
+morkRow::InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace,
+  mork_size inLength, morkPool* ioPool)
+  // if inLength is nonzero, cells will be allocated from ioPool
+{
+  if ( ioSpace && ioPool && inOid )
+  {
+    if ( inLength <= morkRow_kMaxLength )
+    {
+      if ( inOid->mOid_Id != morkRow_kMinusOneRid )
+      {
+        mRow_Space = ioSpace;
+        mRow_Object = 0;
+        mRow_Cells = 0;
+        mRow_Oid = *inOid;
+
+        mRow_Length = (mork_u2) inLength;
+        mRow_Seed = (mork_u2) (mork_ip) this; // "random" assignment
+
+        mRow_GcUses = 0;
+        mRow_Pad = 0;
+        mRow_Flags = 0;
+        mRow_Tag = morkRow_kTag;
+        
+        morkZone* zone = &ioSpace->mSpace_Store->mStore_Zone;
+
+        if ( inLength )
+          mRow_Cells = ioPool->NewCells(ev, inLength, zone);
+
+        if ( this->MaybeDirtySpaceStoreAndRow() ) // new row might dirty store
+        {
+          this->SetRowRewrite();
+          this->NoteRowSetAll(ev);
+        }
+      }
+      else
+        ioSpace->MinusOneRidError(ev);
+    }
+    else
+      this->LengthBeyondMaxError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+morkRowObject*
+morkRow::AcquireRowObject(morkEnv* ev, morkStore* ioStore)
+{
+  morkRowObject* ro = mRow_Object;
+  if ( ro ) // need new row object?
+    ro->AddRef();
+  else
+  {
+    nsIMdbHeap* heap = ioStore->mPort_Heap;
+    ro = new(*heap, ev)
+      morkRowObject(ev, morkUsage::kHeap, heap, this, ioStore);
+    if ( !ro )
+      return (morkRowObject*) 0;
+
+    morkRowObject::SlotWeakRowObject(ro, ev, &mRow_Object);
+    ro->AddRef();
+  }
+  return ro;
+}
+
+nsIMdbRow*
+morkRow::AcquireRowHandle(morkEnv* ev, morkStore* ioStore)
+{
+  return AcquireRowObject(ev, ioStore);
+}
+
+nsIMdbCell*
+morkRow::AcquireCellHandle(morkEnv* ev, morkCell* ioCell,
+  mdb_column inCol, mork_pos inPos)
+{
+  nsIMdbHeap* heap = ev->mEnv_Heap;
+  morkCellObject* cellObj = new(*heap, ev)
+    morkCellObject(ev, morkUsage::kHeap, heap, this, ioCell, inCol, inPos);
+  if ( cellObj )
+  {
+    nsIMdbCell* cellHandle = cellObj->AcquireCellHandle(ev);
+//    cellObj->CutStrongRef(ev->AsMdbEnv());
+    return cellHandle;
+  }
+  return (nsIMdbCell*) 0;
+}
+
+mork_count
+morkRow::CountOverlap(morkEnv* ev, morkCell* ioVector, mork_fill inFill)
+  // Count cells in ioVector that change existing cells in this row when
+  // ioVector is added to the row (as in TakeCells()).   This is the set
+  // of cells with the same columns in ioVector and mRow_Cells, which do
+  // not have exactly the same value in mCell_Atom, and which do not both
+  // have change status equal to morkChange_kCut (because cutting a cut
+  // cell still yields a cell that has been cut).  CountOverlap() also
+  // modifies the change attribute of any cell in ioVector to kDup when
+  // the change was previously kCut and the same column cell was found
+  // in this row with change also equal to kCut; this tells callers later
+  // they need not look for that cell in the row again on a second pass.
+{
+  mork_count outCount = 0;
+  mork_pos pos = 0; // needed by GetCell()
+  morkCell* cells = ioVector;
+  morkCell* end = cells + inFill;
+  --cells; // prepare for preincrement
+  while ( ++cells < end && ev->Good() )
+  {
+    mork_column col = cells->GetColumn();
+    
+    morkCell* old = this->GetCell(ev, col, &pos);
+    if ( old ) // same column?
+    {
+      mork_change newChg = cells->GetChange();
+      mork_change oldChg = old->GetChange();
+      if ( newChg != morkChange_kCut || oldChg != newChg ) // not cut+cut?
+      {
+        if ( cells->mCell_Atom != old->mCell_Atom ) // not same atom?
+          ++outCount; // cells will replace old significantly when added
+      }
+      else
+        cells->SetColumnAndChange(col, morkChange_kDup); // note dup status
+    }
+  }
+  return outCount;
+}
+
+void
+morkRow::MergeCells(morkEnv* ev, morkCell* ioVector,
+  mork_fill inVecLength, mork_fill inOldRowFill, mork_fill inOverlap)
+  // MergeCells() is the part of TakeCells() that does the insertion.
+  // inOldRowFill is the old value of mRow_Length, and inOverlap is the
+  // number of cells in the intersection that must be updated.
+{
+  morkCell* newCells = mRow_Cells + inOldRowFill; // 1st new cell in row
+  morkCell* newEnd = newCells + mRow_Length; // one past last cell
+
+  morkCell* srcCells = ioVector;
+  morkCell* srcEnd = srcCells + inVecLength;
+  
+  --srcCells; // prepare for preincrement
+  while ( ++srcCells < srcEnd && ev->Good() )
+  {
+    mork_change srcChg = srcCells->GetChange();
+    if ( srcChg != morkChange_kDup ) // anything to be done?
+    {
+      morkCell* dstCell = 0;
+      if ( inOverlap )
+      {
+        mork_pos pos = 0; // needed by GetCell()
+        dstCell = this->GetCell(ev, srcCells->GetColumn(), &pos);
+      }
+      if ( dstCell )
+      {
+        --inOverlap; // one fewer intersections to resolve
+        // swap the atoms in the cells to avoid ref counting here:
+        morkAtom* dstAtom = dstCell->mCell_Atom;
+        *dstCell = *srcCells; // bitwise copy, taking src atom
+        srcCells->mCell_Atom = dstAtom; // forget cell ref, if any
+      }
+      else if ( newCells < newEnd ) // another new cell exists?
+      {
+        dstCell = newCells++; // alloc another new cell
+        // take atom from source cell, transferring ref to this row:
+        *dstCell = *srcCells; // bitwise copy, taking src atom
+        srcCells->mCell_Atom = 0; // forget cell ref, if any
+      }
+      else // oops, we ran out...
+        ev->NewError("out of new cells");
+    }
+  }
+}
+
+void
+morkRow::TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength,
+  morkStore* ioStore)
+{
+  if ( ioVector && inVecLength && ev->Good() )
+  {
+    ++mRow_Seed; // intend to change structure of mRow_Cells
+    mork_size length = (mork_size) mRow_Length;
+    
+    mork_count overlap = this->CountOverlap(ev, ioVector, inVecLength);
+
+    mork_size growth = inVecLength - overlap; // cells to add
+    mork_size newLength = length + growth;
+    
+    if ( growth && ev->Good() ) // need to add any cells?
+    {
+      morkZone* zone = &ioStore->mStore_Zone;
+      morkPool* pool = ioStore->StorePool();
+      if ( !pool->AddRowCells(ev, this, length + growth, zone) )
+        ev->NewError("cannot take cells");
+    }
+    if ( ev->Good() )
+    {
+      if ( mRow_Length >= newLength )
+        this->MergeCells(ev, ioVector, inVecLength, length, overlap);
+      else
+        ev->NewError("not enough new cells");
+    }
+  }
+}
+
+mork_bool morkRow::MaybeDirtySpaceStoreAndRow()
+{
+  morkRowSpace* rowSpace = mRow_Space;
+  if ( rowSpace )
+  {
+    morkStore* store = rowSpace->mSpace_Store;
+    if ( store && store->mStore_CanDirty )
+    {
+      store->SetStoreDirty();
+      rowSpace->mSpace_CanDirty = morkBool_kTrue;
+    }
+    
+    if ( rowSpace->mSpace_CanDirty )
+    {
+      this->SetRowDirty();
+      rowSpace->SetRowSpaceDirty();
+      return morkBool_kTrue;
+    }
+  }
+  return morkBool_kFalse;
+}
+
+morkCell*
+morkRow::NewCell(morkEnv* ev, mdb_column inColumn,
+  mork_pos* outPos, morkStore* ioStore)
+{
+  ++mRow_Seed; // intend to change structure of mRow_Cells
+  mork_size length = (mork_size) mRow_Length;
+  *outPos = (mork_pos) length;
+  morkPool* pool = ioStore->StorePool();
+  morkZone* zone = &ioStore->mStore_Zone;
+  
+  mork_bool canDirty = this->MaybeDirtySpaceStoreAndRow();
+  
+  if ( pool->AddRowCells(ev, this, length + 1, zone) )
+  {
+    morkCell* cell = mRow_Cells + length;
+    // next line equivalent to inline morkCell::SetCellDirty():
+    if ( canDirty )
+      cell->SetCellColumnDirty(inColumn);
+    else
+      cell->SetCellColumnClean(inColumn);
+      
+    if ( canDirty && !this->IsRowRewrite() )
+      this->NoteRowAddCol(ev, inColumn);
+      
+    return cell;
+  }
+    
+  return (morkCell*) 0;
+}
+
+
+
+void morkRow::SeekColumn(morkEnv* ev, mdb_pos inPos, 
+  mdb_column* outColumn, mdbYarn* outYarn)
+{
+  morkCell* cells = mRow_Cells;
+  if ( cells && inPos < mRow_Length && inPos >= 0 )
+  {
+    morkCell* c = cells + inPos;
+    if ( outColumn )
+    	*outColumn = c->GetColumn();
+    if ( outYarn )
+    	c->mCell_Atom->GetYarn(outYarn); // nil atom works okay here
+  }
+  else
+  {
+    if ( outColumn )
+    	*outColumn = 0;
+    if ( outYarn )
+    	((morkAtom*) 0)->GetYarn(outYarn); // yes this will work
+  }
+}
+
+void
+morkRow::NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn)
+{
+  morkCell* cells = mRow_Cells;
+  if ( cells )
+  {
+  	mork_column last = 0;
+  	mork_column inCol = *ioColumn;
+    morkCell* end = cells + mRow_Length;
+    while ( cells < end )
+    {
+      if ( inCol == last ) // found column?
+      {
+		    if ( outYarn )
+		    	cells->mCell_Atom->GetYarn(outYarn); // nil atom works okay here
+        *ioColumn = cells->GetColumn();
+        return;  // stop, we are done
+      }
+      else
+      {
+        last = cells->GetColumn();
+        ++cells;
+      }
+    }
+  }
+	*ioColumn = 0;
+  if ( outYarn )
+  	((morkAtom*) 0)->GetYarn(outYarn); // yes this will work
+}
+
+morkCell*
+morkRow::CellAt(morkEnv* ev, mork_pos inPos) const
+{
+  MORK_USED_1(ev);
+  morkCell* cells = mRow_Cells;
+  if ( cells && inPos < mRow_Length && inPos >= 0 )
+  {
+    return cells + inPos;
+  }
+  return (morkCell*) 0;
+}
+
+morkCell*
+morkRow::GetCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos) const
+{
+  MORK_USED_1(ev);
+  morkCell* cells = mRow_Cells;
+  if ( cells )
+  {
+    morkCell* end = cells + mRow_Length;
+    while ( cells < end )
+    {
+      mork_column col = cells->GetColumn();
+      if ( col == inColumn ) // found the desired column?
+      {
+        *outPos = cells - mRow_Cells;
+        return cells;
+      }
+      else
+        ++cells;
+    }
+  }
+  *outPos = -1;
+  return (morkCell*) 0;
+}
+
+mork_aid
+morkRow::GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const
+  // GetCellAtomAid() finds the cell with column inColumn, and sees if the
+  // atom has a token ID, and returns the atom's ID if there is one.  Or
+  // else zero is returned if there is no such column, or no atom, or if
+  // the atom has no ID to return.  This method is intended to support
+  // efficient updating of column indexes for rows in a row space.
+{
+  if ( this && this->IsRow() )
+  {
+    morkCell* cells = mRow_Cells;
+    if ( cells )
+    {
+      morkCell* end = cells + mRow_Length;
+      while ( cells < end )
+      {
+        mork_column col = cells->GetColumn();
+        if ( col == inColumn ) // found desired column?
+        {
+          morkAtom* atom = cells->mCell_Atom;
+          if ( atom && atom->IsBook() )
+            return ((morkBookAtom*) atom)->mBookAtom_Id;
+          else
+            return 0;
+        }
+        else
+          ++cells;
+      }
+    }
+  }
+  else
+    this->NonRowTypeError(ev);
+
+  return 0;
+}
+
+void
+morkRow::EmptyAllCells(morkEnv* ev)
+{
+  morkCell* cells = mRow_Cells;
+  if ( cells )
+  {
+    morkStore* store = this->GetRowSpaceStore(ev);
+    if ( store )
+    {
+      if ( this->MaybeDirtySpaceStoreAndRow() )
+      {
+        this->SetRowRewrite();
+        this->NoteRowSetAll(ev);
+      }
+      morkPool* pool = store->StorePool();
+      morkCell* end = cells + mRow_Length;
+      --cells; // prepare for preincrement:
+      while ( ++cells < end )
+      {
+        if ( cells->mCell_Atom )
+          cells->SetAtom(ev, (morkAtom*) 0, pool);
+      }
+    }
+  }
+}
+
+void 
+morkRow::cut_all_index_entries(morkEnv* ev)
+{
+  morkRowSpace* rowSpace = mRow_Space;
+  if ( rowSpace->mRowSpace_IndexCount ) // any indexes?
+  {
+    morkCell* cells = mRow_Cells;
+    if ( cells )
+    {
+      morkCell* end = cells + mRow_Length;
+      --cells; // prepare for preincrement:
+      while ( ++cells < end )
+      {
+        morkAtom* atom = cells->mCell_Atom;
+        if ( atom )
+        {
+          mork_aid atomAid = atom->GetBookAtomAid();
+          if ( atomAid )
+          {
+            mork_column col = cells->GetColumn();
+            morkAtomRowMap* map = rowSpace->FindMap(ev, col);
+            if ( map ) // cut row from index for this column?
+              map->CutAid(ev, atomAid);
+          }
+        }
+      }
+    }
+  }
+}
+
+void
+morkRow::CutAllColumns(morkEnv* ev)
+{
+  morkStore* store = this->GetRowSpaceStore(ev);
+  if ( store )
+  {
+    if ( this->MaybeDirtySpaceStoreAndRow() )
+    {
+      this->SetRowRewrite();
+      this->NoteRowSetAll(ev);
+    }
+    morkRowSpace* rowSpace = mRow_Space;
+    if ( rowSpace->mRowSpace_IndexCount ) // any indexes?
+      this->cut_all_index_entries(ev);
+  
+    morkPool* pool = store->StorePool();
+    pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone);
+  }
+}
+
+void
+morkRow::SetRow(morkEnv* ev, const morkRow* inSourceRow)
+{  
+  // note inSourceRow might be in another DB, with a different store...
+  morkStore* store = this->GetRowSpaceStore(ev);
+  morkStore* srcStore = inSourceRow->GetRowSpaceStore(ev);
+  if ( store && srcStore )
+  {
+    if ( this->MaybeDirtySpaceStoreAndRow() )
+    {
+      this->SetRowRewrite();
+      this->NoteRowSetAll(ev);
+    }
+    morkRowSpace* rowSpace = mRow_Space;
+    mork_count indexes = rowSpace->mRowSpace_IndexCount; // any indexes?
+    
+    mork_bool sameStore = ( store == srcStore ); // identical stores?
+    morkPool* pool = store->StorePool();
+    if ( pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone) )
+    {
+      mork_fill fill = inSourceRow->mRow_Length;
+      if ( pool->AddRowCells(ev, this, fill, &store->mStore_Zone) )
+      {
+        morkCell* dst = mRow_Cells;
+        morkCell* dstEnd = dst + mRow_Length;
+        
+        const morkCell* src = inSourceRow->mRow_Cells;
+        const morkCell* srcEnd = src + fill;
+        --dst; --src; // prepare both for preincrement:
+        
+        while ( ++dst < dstEnd && ++src < srcEnd && ev->Good() )
+        {
+          morkAtom* atom = src->mCell_Atom;
+          mork_column dstCol = src->GetColumn();
+          // Note we modify the mCell_Atom slot directly instead of using
+          // morkCell::SetAtom(), because we know it starts equal to nil.
+          
+          if ( sameStore ) // source and dest in same store?
+          {
+            // next line equivalent to inline morkCell::SetCellDirty():
+            dst->SetCellColumnDirty(dstCol);
+            dst->mCell_Atom = atom;
+            if ( atom ) // another ref to non-nil atom?
+              atom->AddCellUse(ev);
+          }
+          else // need to dup items from src store in a dest store
+          {
+            dstCol = store->CopyToken(ev, dstCol, srcStore);
+            if ( dstCol )
+            {
+              // next line equivalent to inline morkCell::SetCellDirty():
+              dst->SetCellColumnDirty(dstCol);
+              atom = store->CopyAtom(ev, atom);
+              dst->mCell_Atom = atom;
+              if ( atom ) // another ref?
+                atom->AddCellUse(ev);
+            }
+          }
+          if ( indexes && atom )
+          {
+            mork_aid atomAid = atom->GetBookAtomAid();
+            if ( atomAid )
+            {
+              morkAtomRowMap* map = rowSpace->FindMap(ev, dstCol);
+              if ( map )
+                map->AddAid(ev, atomAid, this);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+void
+morkRow::AddRow(morkEnv* ev, const morkRow* inSourceRow)
+{
+  if ( mRow_Length ) // any existing cells we might need to keep?
+  {
+    ev->StubMethodOnlyError();
+  }
+  else
+    this->SetRow(ev, inSourceRow); // just exactly duplicate inSourceRow
+}
+
+void
+morkRow::OnZeroRowGcUse(morkEnv* ev)
+// OnZeroRowGcUse() is called when CutRowGcUse() returns zero.
+{
+  MORK_USED_1(ev);
+  // ev->NewWarning("need to implement OnZeroRowGcUse");
+}
+
+void
+morkRow::DirtyAllRowContent(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+
+  if ( this->MaybeDirtySpaceStoreAndRow() )
+  {
+    this->SetRowRewrite();
+    this->NoteRowSetAll(ev);
+  }
+  morkCell* cells = mRow_Cells;
+  if ( cells )
+  {
+    morkCell* end = cells + mRow_Length;
+    --cells; // prepare for preincrement:
+    while ( ++cells < end )
+    {
+      cells->SetCellDirty();
+    }
+  }
+}
+
+morkStore*
+morkRow::GetRowSpaceStore(morkEnv* ev) const
+{
+  morkRowSpace* rowSpace = mRow_Space;
+  if ( rowSpace )
+  {
+    morkStore* store = rowSpace->mSpace_Store;
+    if ( store )
+    {
+      if ( store->IsStore() )
+      {
+        return store;
+      }
+      else
+        store->NonStoreTypeError(ev);
+    }
+    else
+      ev->NilPointerError();
+  }
+  else
+    ev->NilPointerError();
+    
+  return (morkStore*) 0;
+}
+
+void morkRow::CutColumn(morkEnv* ev, mdb_column inColumn)
+{
+  mork_pos pos = -1;
+  morkCell* cell = this->GetCell(ev, inColumn, &pos);
+  if ( cell ) 
+  {
+    morkStore* store = this->GetRowSpaceStore(ev);
+    if ( store )
+    {
+      if ( this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite() )
+        this->NoteRowCutCol(ev, inColumn);
+        
+      morkRowSpace* rowSpace = mRow_Space;
+      morkAtomRowMap* map = ( rowSpace->mRowSpace_IndexCount )?
+        rowSpace->FindMap(ev, inColumn) : (morkAtomRowMap*) 0;
+      if ( map ) // this row attribute is indexed by row space?
+      {
+        morkAtom* oldAtom = cell->mCell_Atom;
+        if ( oldAtom ) // need to cut an entry from the index?
+        {
+          mork_aid oldAid = oldAtom->GetBookAtomAid();
+          if ( oldAid ) // cut old row attribute from row index in space?
+            map->CutAid(ev, oldAid);
+        }
+      }
+      
+      morkPool* pool = store->StorePool();
+      cell->SetAtom(ev, (morkAtom*) 0, pool);
+      
+      mork_fill fill = mRow_Length; // should not be zero
+      MORK_ASSERT(fill);
+      if ( fill ) // index < fill for last cell exists?
+      {
+        mork_fill last = fill - 1; // index of last cell in row
+        
+        if ( pos < (mork_pos)last ) // need to move cells following cut cell?
+        {
+          morkCell* lastCell = mRow_Cells + last;
+          mork_count after = last - pos; // cell count after cut cell
+          morkCell* next = cell + 1; // next cell after cut cell
+          MORK_MEMMOVE(cell, next, after * sizeof(morkCell));
+          lastCell->SetColumnAndChange(0, 0);
+          lastCell->mCell_Atom = 0;
+        }
+        
+        if ( ev->Good() )
+          pool->CutRowCells(ev, this, fill - 1, &store->mStore_Zone);
+      }
+    }
+  }
+}
+
+morkAtom* morkRow::GetColumnAtom(morkEnv* ev, mdb_column inColumn)
+{
+  if ( ev->Good() )
+  {
+    mork_pos pos = -1;
+    morkCell* cell = this->GetCell(ev, inColumn, &pos);
+    if ( cell )
+    	return cell->mCell_Atom;
+  }
+  return (morkAtom*) 0;
+}
+
+void morkRow::AddColumn(morkEnv* ev, mdb_column inColumn,
+  const mdbYarn* inYarn, morkStore* ioStore)
+{
+  if ( ev->Good() )
+  {
+    mork_pos pos = -1;
+    morkCell* cell = this->GetCell(ev, inColumn, &pos);
+    morkCell* oldCell = cell; // need to know later whether new
+    if ( !cell ) // column does not yet exist?
+      cell = this->NewCell(ev, inColumn, &pos, ioStore);
+    
+    if ( cell )
+    {
+      morkAtom* oldAtom = cell->mCell_Atom;
+
+      morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, PR_TRUE /* create */);
+      if ( atom && atom != oldAtom )
+      {
+        morkRowSpace* rowSpace = mRow_Space;
+        morkAtomRowMap* map = ( rowSpace->mRowSpace_IndexCount )?
+          rowSpace->FindMap(ev, inColumn) : (morkAtomRowMap*) 0;
+        
+        if ( map ) // inColumn is indexed by row space?
+        {
+          if ( oldAtom && oldAtom != atom ) // cut old cell from index?
+          {
+            mork_aid oldAid = oldAtom->GetBookAtomAid();
+            if ( oldAid ) // cut old row attribute from row index in space?
+              map->CutAid(ev, oldAid);
+          }
+        }
+        
+        cell->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom
+
+        if ( oldCell ) // we changed a pre-existing cell in the row?
+        {
+          ++mRow_Seed;
+          if ( this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite() )
+            this->NoteRowAddCol(ev, inColumn);
+        }
+
+        if ( map ) // inColumn is indexed by row space?
+        {
+          mork_aid newAid = atom->GetBookAtomAid();
+          if ( newAid ) // add new row attribute to row index in space?
+            map->AddAid(ev, newAid, this);
+        }
+      }
+    }
+  }
+}
+
+morkRowCellCursor*
+morkRow::NewRowCellCursor(morkEnv* ev, mdb_pos inPos)
+{
+  morkRowCellCursor* outCursor = 0;
+  if ( ev->Good() )
+  {
+    morkStore* store = this->GetRowSpaceStore(ev);
+    if ( store )
+    {
+      morkRowObject* rowObj = this->AcquireRowObject(ev, store);
+      if ( rowObj )
+      {
+        nsIMdbHeap* heap = store->mPort_Heap;
+        morkRowCellCursor* cursor = new(*heap, ev)
+          morkRowCellCursor(ev, morkUsage::kHeap, heap, rowObj);
+         
+        if ( cursor )
+        {
+          if ( ev->Good() )
+          {
+            cursor->mRowCellCursor_Col = inPos;
+            outCursor = cursor;
+          }
+          else
+            cursor->CutStrongRef(ev->mEnv_SelfAsMdbEnv);
+        }
+        rowObj->Release(); // always cut ref (cursor has its own)
+      }
+    }
+  }
+  return outCursor;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRow.h
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKROW_
+#define _MORKROW_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class nsIMdbRow;
+class nsIMdbCell;
+#define morkDerived_kRow  /*i*/ 0x5277 /* ascii 'Rw' */
+
+#define morkRow_kMaxGcUses 0x0FF /* max for 8-bit unsigned int */
+#define morkRow_kMaxLength 0x0FFFF /* max for 16-bit unsigned int */
+#define morkRow_kMinusOneRid ((mork_rid) -1)
+
+#define morkRow_kTag 'r' /* magic signature for mRow_Tag */
+
+#define morkRow_kNotedBit   ((mork_u1) (1 << 0)) /* space has change notes */
+#define morkRow_kRewriteBit ((mork_u1) (1 << 1)) /* must rewrite all cells */
+#define morkRow_kDirtyBit   ((mork_u1) (1 << 2)) /* row has been changed */
+
+class morkRow{ // row of cells
+
+public: // state is public because the entire Mork system is private
+
+  morkRowSpace*   mRow_Space;  // mRow_Space->SpaceScope() is the row scope 
+  morkRowObject*  mRow_Object; // refcount & other state for object sharing
+  morkCell*       mRow_Cells;
+  mdbOid          mRow_Oid;
+  
+  mork_delta      mRow_Delta;   // space to note a single column change
+
+  mork_u2         mRow_Length;     // physical count of cells in mRow_Cells 
+  mork_u2         mRow_Seed;       // count changes in mRow_Cells structure
+
+  mork_u1         mRow_GcUses;  // persistent references from tables
+  mork_u1         mRow_Pad;     // for u1 alignment
+  mork_u1         mRow_Flags;   // one-byte flags slot
+  mork_u1         mRow_Tag;     // one-byte tag (need u4 alignment pad)
+
+public: // interpreting mRow_Delta
+  
+  mork_bool HasRowDelta() const { return ( mRow_Delta != 0 ); }
+  
+  void ClearRowDelta() { mRow_Delta = 0; }
+  
+  void SetRowDelta(mork_column inCol, mork_change inChange)
+  { morkDelta_Init(mRow_Delta, inCol, inChange); }
+  
+  mork_column  GetDeltaColumn() const { return morkDelta_Column(mRow_Delta); }
+  mork_change  GetDeltaChange() const { return morkDelta_Change(mRow_Delta); }
+
+public: // noting row changes
+
+  void NoteRowSetAll(morkEnv* ev);
+  void NoteRowSetCol(morkEnv* ev, mork_column inCol);
+  void NoteRowAddCol(morkEnv* ev, mork_column inCol);
+  void NoteRowCutCol(morkEnv* ev, mork_column inCol);
+
+public: // flags bit twiddling
+
+  void SetRowNoted() { mRow_Flags |= morkRow_kNotedBit; }
+  void SetRowRewrite() { mRow_Flags |= morkRow_kRewriteBit; }
+  void SetRowDirty() { mRow_Flags |= morkRow_kDirtyBit; }
+
+  void ClearRowNoted() { mRow_Flags &= (mork_u1) ~morkRow_kNotedBit; }
+  void ClearRowRewrite() { mRow_Flags &= (mork_u1) ~morkRow_kRewriteBit; }
+  void SetRowClean() { mRow_Flags = 0; mRow_Delta = 0; }
+  
+  mork_bool IsRowNoted() const
+  { return ( mRow_Flags & morkRow_kNotedBit ) != 0; }
+  
+  mork_bool IsRowRewrite() const
+  { return ( mRow_Flags & morkRow_kRewriteBit ) != 0; }
+   
+  mork_bool IsRowClean() const
+  { return ( mRow_Flags & morkRow_kDirtyBit ) == 0; }
+  
+  mork_bool IsRowDirty() const
+  { return ( mRow_Flags & morkRow_kDirtyBit ) != 0; }
+  
+  mork_bool IsRowUsed() const
+  { return mRow_GcUses != 0; }
+
+public: // other row methods
+  morkRow( ) { }
+  morkRow(const mdbOid* inOid) :mRow_Oid(*inOid) { }
+  void InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace,
+    mork_size inLength, morkPool* ioPool);
+    // if inLength is nonzero, cells will be allocated from ioPool
+
+  morkRowObject* AcquireRowObject(morkEnv* ev, morkStore* ioStore);
+  nsIMdbRow* AcquireRowHandle(morkEnv* ev, morkStore* ioStore);
+  nsIMdbCell* AcquireCellHandle(morkEnv* ev, morkCell* ioCell,
+    mdb_column inColumn, mork_pos inPos);
+  
+  mork_u2 AddRowGcUse(morkEnv* ev);
+  mork_u2 CutRowGcUse(morkEnv* ev);
+
+  
+  mork_bool MaybeDirtySpaceStoreAndRow();
+
+public: // internal row methods
+
+  void cut_all_index_entries(morkEnv* ev);
+
+  // void cut_cell_from_space_index(morkEnv* ev, morkCell* ioCell);
+
+  mork_count CountOverlap(morkEnv* ev, morkCell* ioVector, mork_fill inFill);
+  // Count cells in ioVector that change existing cells in this row when
+  // ioVector is added to the row (as in TakeCells()).   This is the set
+  // of cells with the same columns in ioVector and mRow_Cells, which do
+  // not have exactly the same value in mCell_Atom, and which do not both
+  // have change status equal to morkChange_kCut (because cutting a cut
+  // cell still yields a cell that has been cut).  CountOverlap() also
+  // modifies the change attribute of any cell in ioVector to kDup when
+  // the change was previously kCut and the same column cell was found
+  // in this row with change also equal to kCut; this tells callers later
+  // they need not look for that cell in the row again on a second pass.
+
+  void MergeCells(morkEnv* ev, morkCell* ioVector,
+    mork_fill inVecLength, mork_fill inOldRowFill, mork_fill inOverlap);
+  // MergeCells() is the part of TakeCells() that does the insertion.
+  // inOldRowFill is the old value of mRow_Length, and inOverlap is the
+  // number of cells in the intersection that must be updated.
+
+  void TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength,
+    morkStore* ioStore);
+
+  morkCell* NewCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos,
+    morkStore* ioStore);
+  morkCell* GetCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos) const;
+  morkCell* CellAt(morkEnv* ev, mork_pos inPos) const;
+
+  mork_aid GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const;
+  // GetCellAtomAid() finds the cell with column inColumn, and sees if the
+  // atom has a token ID, and returns the atom's ID if there is one.  Or
+  // else zero is returned if there is no such column, or no atom, or if
+  // the atom has no ID to return.  This method is intended to support
+  // efficient updating of column indexes for rows in a row space.
+
+public: // external row methods
+
+  void DirtyAllRowContent(morkEnv* ev);
+
+  morkStore* GetRowSpaceStore(morkEnv* ev) const;
+
+  void AddColumn(morkEnv* ev, mdb_column inColumn,
+    const mdbYarn* inYarn, morkStore* ioStore);
+
+  morkAtom* GetColumnAtom(morkEnv* ev, mdb_column inColumn);
+
+  void NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn);
+
+  void SeekColumn(morkEnv* ev, mdb_pos inPos, 
+	  mdb_column* outColumn, mdbYarn* outYarn);
+
+  void CutColumn(morkEnv* ev, mdb_column inColumn);
+
+  morkRowCellCursor* NewRowCellCursor(morkEnv* ev, mdb_pos inPos);
+  
+  void EmptyAllCells(morkEnv* ev);
+  void AddRow(morkEnv* ev, const morkRow* inSourceRow);
+  void SetRow(morkEnv* ev, const morkRow* inSourceRow);
+  void CutAllColumns(morkEnv* ev);
+
+  void OnZeroRowGcUse(morkEnv* ev);
+  // OnZeroRowGcUse() is called when CutRowGcUse() returns zero.
+
+public: // dynamic typing
+
+  mork_bool IsRow() const { return mRow_Tag == morkRow_kTag; }
+
+public: // hash and equal
+
+  mork_u4 HashRow() const
+  {
+    return (mRow_Oid.mOid_Scope << 16) ^ mRow_Oid.mOid_Id;
+  }
+
+  mork_bool EqualRow(const morkRow* ioRow) const
+  {
+    return
+    (
+      ( mRow_Oid.mOid_Scope == ioRow->mRow_Oid.mOid_Scope ) 
+      && ( mRow_Oid.mOid_Id == ioRow->mRow_Oid.mOid_Id )
+    );
+  }
+
+  mork_bool EqualOid(const mdbOid* ioOid) const
+  {
+    return
+    (
+      ( mRow_Oid.mOid_Scope == ioOid->mOid_Scope ) 
+      && ( mRow_Oid.mOid_Id == ioOid->mOid_Id )
+    );
+  }
+
+public: // errors
+  static void ZeroColumnError(morkEnv* ev);
+  static void LengthBeyondMaxError(morkEnv* ev);
+  static void NilCellsError(morkEnv* ev);
+  static void NonRowTypeError(morkEnv* ev);
+  static void NonRowTypeWarning(morkEnv* ev);
+  static void GcUsesUnderflowWarning(morkEnv* ev);
+
+private: // copying is not allowed
+  morkRow(const morkRow& other);
+  morkRow& operator=(const morkRow& other);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROW_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRowCellCursor.cpp
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKROWCELLCURSOR_
+#include "morkRowCellCursor.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowCellCursor::CloseMorkNode(morkEnv* ev) // CloseRowCellCursor() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseRowCellCursor(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkRowCellCursor::~morkRowCellCursor() // CloseRowCellCursor() executed earlier
+{
+  CloseMorkNode(mMorkEnv);
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowCellCursor::morkRowCellCursor(morkEnv* ev,
+  const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, morkRowObject* ioRowObject)
+: morkCursor(ev, inUsage, ioHeap)
+, mRowCellCursor_RowObject( 0 )
+, mRowCellCursor_Col( 0 )
+{
+  if ( ev->Good() )
+  {
+    if ( ioRowObject )
+    {
+      morkRow* row = ioRowObject->mRowObject_Row;
+      if ( row )
+      {
+        if ( row->IsRow() )
+        {
+          mCursor_Pos = -1;
+          mCursor_Seed = row->mRow_Seed;
+          
+          morkRowObject::SlotStrongRowObject(ioRowObject, ev,
+            &mRowCellCursor_RowObject);
+          if ( ev->Good() )
+            mNode_Derived = morkDerived_kRowCellCursor;
+        }
+        else
+          row->NonRowTypeError(ev);
+      }
+      else
+        ioRowObject->NilRowError(ev);
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkRowCellCursor, morkCursor, nsIMdbRowCellCursor)
+
+/*public non-poly*/ void
+morkRowCellCursor::CloseRowCellCursor(morkEnv* ev) 
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mCursor_Pos = -1;
+      mCursor_Seed = 0;
+      morkRowObject::SlotStrongRowObject((morkRowObject*) 0, ev,
+        &mRowCellCursor_RowObject);
+      this->CloseCursor(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkRowCellCursor::NilRowObjectError(morkEnv* ev)
+{
+  ev->NewError("nil mRowCellCursor_RowObject");
+}
+
+/*static*/ void
+morkRowCellCursor::NonRowCellCursorTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkRowCellCursor");
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkRowCellCursor::SetRow(nsIMdbEnv* mev, nsIMdbRow* ioRow)
+{
+  mdb_err outErr = 0;
+  morkRow* row = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    row = (morkRow *) ioRow;
+    morkStore* store = row->GetRowSpaceStore(ev);
+    if ( store )
+    {
+      morkRowObject* rowObj = row->AcquireRowObject(ev, store);
+      if ( rowObj )
+      {
+        morkRowObject::SlotStrongRowObject((morkRowObject*) 0, ev,
+          &mRowCellCursor_RowObject);
+          
+        mRowCellCursor_RowObject = rowObj; // take this strong ref
+        mCursor_Seed = row->mRow_Seed;
+        
+        row->GetCell(ev, mRowCellCursor_Col, &mCursor_Pos);
+      }
+    }
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkRowCellCursor::GetRow(nsIMdbEnv* mev, nsIMdbRow** acqRow)
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRowObject* rowObj = mRowCellCursor_RowObject;
+    if ( rowObj )
+      outRow = rowObj->AcquireRowHandle(ev);
+
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin cell creation methods -----
+NS_IMETHODIMP
+morkRowCellCursor::MakeCell( // get cell at current pos in the row
+  nsIMdbEnv* mev, // context
+  mdb_column* outColumn, // column for this particular cell
+  mdb_pos* outPos, // position of cell in row sequence
+  nsIMdbCell** acqCell)
+{
+  mdb_err outErr = 0;
+  nsIMdbCell* outCell = 0;
+  mdb_pos pos = 0;
+  mdb_column col = 0;
+  morkRow* row = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    pos = mCursor_Pos;
+    morkCell* cell = row->CellAt(ev, pos);
+    if ( cell )
+    {
+      col = cell->GetColumn();
+      outCell = row->AcquireCellHandle(ev, cell, col, pos);
+    }
+    outErr = ev->AsErr();
+  }
+  if ( acqCell )
+    *acqCell = outCell;
+   if ( outPos )
+     *outPos = pos;
+   if ( outColumn )
+     *outColumn = col;
+     
+  return outErr;
+}
+// } ----- end cell creation methods -----
+
+// { ----- begin cell seeking methods -----
+NS_IMETHODIMP
+morkRowCellCursor::SeekCell( // same as SetRow() followed by MakeCell()
+  nsIMdbEnv* mev, // context
+  mdb_pos inPos, // position of cell in row sequence
+  mdb_column* outColumn, // column for this particular cell
+  nsIMdbCell** acqCell)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end cell seeking methods -----
+
+// { ----- begin cell iteration methods -----
+NS_IMETHODIMP
+morkRowCellCursor::NextCell( // get next cell in the row
+  nsIMdbEnv* mev, // context
+  nsIMdbCell** acqCell, // changes to the next cell in the iteration
+  mdb_column* outColumn, // column for this particular cell
+  mdb_pos* outPos)
+{
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  mdb_column col = 0;
+  mdb_pos pos = mRowCellCursor_Col;
+  if ( pos < 0 )
+    pos = 0;
+  else
+    ++pos;
+
+  morkCell* cell = mRowCellCursor_RowObject->mRowObject_Row->CellAt(ev, pos);
+  if ( cell )
+  {
+    col = cell->GetColumn();
+    *acqCell = mRowCellCursor_RowObject->mRowObject_Row->AcquireCellHandle(ev, cell, col, pos);
+  }
+  else
+  {
+    *acqCell = nsnull;
+    pos = -1;
+  }
+ if ( outPos )
+   *outPos = pos;
+ if ( outColumn )
+   *outColumn = col;
+     
+  mRowCellCursor_Col = pos;
+  return NS_OK;
+}
+  
+NS_IMETHODIMP
+morkRowCellCursor::PickNextCell( // get next cell in row within filter set
+  nsIMdbEnv* mev, // context
+  nsIMdbCell* ioCell, // changes to the next cell in the iteration
+  const mdbColumnSet* inFilterSet, // col set of actual caller interest
+  mdb_column* outColumn, // column for this particular cell
+  mdb_pos* outPos)
+// Note that inFilterSet should not have too many (many more than 10?)
+// cols, since this might imply a potential excessive consumption of time
+// over many cursor calls when looking for column and filter intersection.
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// } ----- end cell iteration methods -----
+
+// } ===== end nsIMdbRowCellCursor methods =====
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRowCellCursor.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKROWCELLCURSOR_
+#define _MORKROWCELLCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class orkinRowCellCursor;
+#define morkDerived_kRowCellCursor  /*i*/ 0x6343 /* ascii 'cC' */
+
+class morkRowCellCursor : public morkCursor, public nsIMdbRowCellCursor { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+  // morkFactory* mObject_Factory;  // weak ref to suite factory
+
+  // mork_seed  mCursor_Seed;
+  // mork_pos   mCursor_Pos;
+  // mork_bool  mCursor_DoFailOnSeedOutOfSync;
+  // mork_u1    mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+
+  NS_DECL_ISUPPORTS_INHERITED
+  morkRowObject*   mRowCellCursor_RowObject;  // strong ref to row
+  mork_column      mRowCellCursor_Col;        // col of cell last at mCursor_Pos
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseRowCellCursor()
+  virtual ~morkRowCellCursor(); // assert that close executed earlier
+  
+public: // morkRowCellCursor construction & destruction
+  morkRowCellCursor(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkRowObject* ioRowObject);
+  void CloseRowCellCursor(morkEnv* ev); // called by CloseMorkNode();
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD SetRow(nsIMdbEnv* ev, nsIMdbRow* ioRow); // sets pos to -1
+  NS_IMETHOD GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow);
+  // } ----- end attribute methods -----
+
+  // { ----- begin cell creation methods -----
+  NS_IMETHOD MakeCell( // get cell at current pos in the row
+    nsIMdbEnv* ev, // context
+    mdb_column* outColumn, // column for this particular cell
+    mdb_pos* outPos, // position of cell in row sequence
+    nsIMdbCell** acqCell); // the cell at inPos
+  // } ----- end cell creation methods -----
+
+  // { ----- begin cell seeking methods -----
+  NS_IMETHOD SeekCell( // same as SetRow() followed by MakeCell()
+    nsIMdbEnv* ev, // context
+    mdb_pos inPos, // position of cell in row sequence
+    mdb_column* outColumn, // column for this particular cell
+    nsIMdbCell** acqCell); // the cell at inPos
+  // } ----- end cell seeking methods -----
+
+  // { ----- begin cell iteration methods -----
+  NS_IMETHOD NextCell( // get next cell in the row
+    nsIMdbEnv* ev, // context
+    nsIMdbCell** acqCell, // changes to the next cell in the iteration
+    mdb_column* outColumn, // column for this particular cell
+    mdb_pos* outPos); // position of cell in row sequence
+    
+  NS_IMETHOD PickNextCell( // get next cell in row within filter set
+    nsIMdbEnv* ev, // context
+    nsIMdbCell* ioCell, // changes to the next cell in the iteration
+    const mdbColumnSet* inFilterSet, // col set of actual caller interest
+    mdb_column* outColumn, // column for this particular cell
+    mdb_pos* outPos); // position of cell in row sequence
+
+  // Note that inFilterSet should not have too many (many more than 10?)
+  // cols, since this might imply a potential excessive consumption of time
+  // over many cursor calls when looking for column and filter intersection.
+  // } ----- end cell iteration methods -----
+
+
+private: // copying is not allowed
+  morkRowCellCursor(const morkRowCellCursor& other);
+  morkRowCellCursor& operator=(const morkRowCellCursor& other);
+
+public: // dynamic type identification
+  mork_bool IsRowCellCursor() const
+  { return IsNode() && mNode_Derived == morkDerived_kRowCellCursor; }
+// } ===== end morkNode methods =====
+
+public: // errors
+  static void NilRowObjectError(morkEnv* ev);
+  static void NonRowCellCursorTypeError(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakRowCellCursor(morkRowCellCursor* me,
+    morkEnv* ev, morkRowCellCursor** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongRowCellCursor(morkRowCellCursor* me,
+    morkEnv* ev, morkRowCellCursor** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROWCELLCURSOR_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRowMap.cpp
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowMap::CloseMorkNode(morkEnv* ev) // CloseRowMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseRowMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkRowMap::~morkRowMap() // assert CloseRowMap() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowMap::morkRowMap(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots)
+: morkMap(ev, inUsage,  ioHeap,
+  /*inKeySize*/ sizeof(morkRow*), /*inValSize*/ 0,
+  inSlots, ioSlotHeap, /*inHoldChanges*/ morkBool_kFalse)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kRowMap;
+}
+
+/*public non-poly*/ void
+morkRowMap::CloseRowMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      this->CloseMap(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+
+// { ===== begin morkMap poly interface =====
+/*virtual*/ mork_bool // 
+morkRowMap::Equal(morkEnv* ev, const void* inKeyA,
+  const void* inKeyB) const
+{
+  MORK_USED_1(ev);
+  return (*(const morkRow**) inKeyA)->EqualRow(*(const morkRow**) inKeyB);
+}
+
+/*virtual*/ mork_u4 // 
+morkRowMap::Hash(morkEnv* ev, const void* inKey) const
+{
+  MORK_USED_1(ev);
+  return (*(const morkRow**) inKey)->HashRow();
+}
+// } ===== end morkMap poly interface =====
+
+
+mork_bool
+morkRowMap::AddRow(morkEnv* ev, morkRow* ioRow)
+{
+  if ( ev->Good() )
+  {
+    this->Put(ev, &ioRow, /*val*/ (void*) 0, 
+      /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+  }
+  return ev->Good();
+}
+
+morkRow*
+morkRowMap::CutOid(morkEnv* ev, const mdbOid* inOid)
+{
+  morkRow row(inOid);
+  morkRow* key = &row;
+  morkRow* oldKey = 0;
+  this->Cut(ev, &key, &oldKey, /*val*/ (void*) 0,
+    (mork_change**) 0);
+    
+  return oldKey;
+}
+
+morkRow*
+morkRowMap::CutRow(morkEnv* ev, const morkRow* ioRow)
+{
+  morkRow* oldKey = 0;
+  this->Cut(ev, &ioRow, &oldKey, /*val*/ (void*) 0,
+    (mork_change**) 0);
+    
+  return oldKey;
+}
+
+morkRow*
+morkRowMap::GetOid(morkEnv* ev, const mdbOid* inOid)
+{
+  morkRow row(inOid);
+  morkRow* key = &row;
+  morkRow* oldKey = 0;
+  this->Get(ev, &key, &oldKey, /*val*/ (void*) 0, (mork_change**) 0);
+  
+  return oldKey;
+}
+
+morkRow*
+morkRowMap::GetRow(morkEnv* ev, const morkRow* ioRow)
+{
+  morkRow* oldKey = 0;
+  this->Get(ev, &ioRow, &oldKey, /*val*/ (void*) 0, (mork_change**) 0);
+  
+  return oldKey;
+}
+
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowProbeMap::CloseMorkNode(morkEnv* ev) // CloseRowProbeMap() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseRowProbeMap(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkRowProbeMap::~morkRowProbeMap() // assert CloseRowProbeMap() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowProbeMap::morkRowProbeMap(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots)
+: morkProbeMap(ev, inUsage,  ioHeap,
+  /*inKeySize*/ sizeof(morkRow*), /*inValSize*/ 0,
+  ioSlotHeap, inSlots, 
+  /*inHoldChanges*/ morkBool_kTrue)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kRowProbeMap;
+}
+
+/*public non-poly*/ void
+morkRowProbeMap::CloseRowProbeMap(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      this->CloseProbeMap(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+morkRowProbeMap::MapTest(morkEnv* ev, const void* inMapKey,
+  const void* inAppKey) const
+{
+  MORK_USED_1(ev);
+  const morkRow* key = *(const morkRow**) inMapKey;
+  if ( key )
+  {
+    mork_bool hit = key->EqualRow(*(const morkRow**) inAppKey);
+    return ( hit ) ? morkTest_kHit : morkTest_kMiss;
+  }
+  else
+    return morkTest_kVoid;
+}
+
+/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+morkRowProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const
+{
+  const morkRow* key = *(const morkRow**) inAppKey;
+  if ( key )
+    return key->HashRow();
+  else
+  {
+    ev->NilPointerWarning();
+    return 0;
+  }
+}
+
+/*virtual*/ mork_u4 
+morkRowProbeMap::ProbeMapHashMapKey(morkEnv* ev,
+  const void* inMapKey) const
+{
+  const morkRow* key = *(const morkRow**) inMapKey;
+  if ( key )
+    return key->HashRow();
+  else
+  {
+    ev->NilPointerWarning();
+    return 0;
+  }
+}
+
+mork_bool
+morkRowProbeMap::AddRow(morkEnv* ev, morkRow* ioRow)
+{
+  if ( ev->Good() )
+  {
+    this->MapAtPut(ev, &ioRow, /*val*/ (void*) 0, 
+      /*key*/ (void*) 0, /*val*/ (void*) 0);
+  }
+  return ev->Good();
+}
+
+morkRow*
+morkRowProbeMap::CutOid(morkEnv* ev, const mdbOid* inOid)
+{
+  MORK_USED_1(inOid);
+  morkProbeMap::ProbeMapCutError(ev);
+    
+  return 0;
+}
+
+morkRow*
+morkRowProbeMap::CutRow(morkEnv* ev, const morkRow* ioRow)
+{
+  MORK_USED_1(ioRow);
+  morkProbeMap::ProbeMapCutError(ev);
+    
+  return 0;
+}
+
+morkRow*
+morkRowProbeMap::GetOid(morkEnv* ev, const mdbOid* inOid)
+{
+  morkRow row(inOid);
+  morkRow* key = &row;
+  morkRow* oldKey = 0;
+  this->MapAt(ev, &key, &oldKey, /*val*/ (void*) 0);
+  
+  return oldKey;
+}
+
+morkRow*
+morkRowProbeMap::GetRow(morkEnv* ev, const morkRow* ioRow)
+{
+  morkRow* oldKey = 0;
+  this->MapAt(ev, &ioRow, &oldKey, /*val*/ (void*) 0);
+  
+  return oldKey;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRowMap.h
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKROWMAP_
+#define _MORKROWMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kRowMap  /*i*/ 0x724D /* ascii 'rM' */
+
+/*| morkRowMap: maps a set of morkRow by contained Oid
+|*/
+class morkRowMap : public morkMap { // for mapping row IDs to rows
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseRowMap() only if open
+  virtual ~morkRowMap(); // assert that CloseRowMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkRowMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots);
+  void CloseRowMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsRowMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kRowMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+  virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b)
+  Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const;
+  // implemented using morkRow::EqualRow()
+
+  virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b)
+  Hash(morkEnv* ev, const void* inKey) const;
+  // implemented using morkRow::HashRow()
+// } ===== end morkMap poly interface =====
+
+public: // other map methods
+
+  mork_bool AddRow(morkEnv* ev, morkRow* ioRow);
+  // AddRow() returns ev->Good()
+
+  morkRow*  CutOid(morkEnv* ev, const mdbOid* inOid);
+  // CutRid() returns the row removed equal to inRid, if there was one
+
+  morkRow*  CutRow(morkEnv* ev, const morkRow* ioRow);
+  // CutRow() returns the row removed equal to ioRow, if there was one
+  
+  morkRow*  GetOid(morkEnv* ev, const mdbOid* inOid);
+  // GetOid() returns the row equal to inRid, or else nil
+  
+  morkRow*  GetRow(morkEnv* ev, const morkRow* ioRow);
+  // GetRow() returns the row equal to ioRow, or else nil
+  
+  // note the rows are owned elsewhere, usuall by morkRowSpace
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakRowMap(morkRowMap* me,
+    morkEnv* ev, morkRowMap** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongRowMap(morkRowMap* me,
+    morkEnv* ev, morkRowMap** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+class morkRowMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+  morkRowMapIter(morkEnv* ev, morkRowMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkRowMapIter( ) : morkMapIter()  { }
+  void InitRowMapIter(morkEnv* ev, morkRowMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change* FirstRow(morkEnv* ev, morkRow** outRowPtr)
+  { return this->First(ev, outRowPtr, /*val*/ (void*) 0); }
+  
+  mork_change* NextRow(morkEnv* ev, morkRow** outRowPtr)
+  { return this->Next(ev, outRowPtr, /*val*/ (void*) 0); }
+  
+  mork_change* HereRow(morkEnv* ev, morkRow** outRowPtr)
+  { return this->Here(ev, outRowPtr, /*val*/ (void*) 0); }
+  
+  mork_change* CutHereRow(morkEnv* ev, morkRow** outRowPtr)
+  { return this->CutHere(ev, outRowPtr, /*val*/ (void*) 0); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kRowProbeMap  /*i*/ 0x726D /* ascii 'rm' */
+
+/*| morkRowProbeMap: maps a set of morkRow by contained Oid
+|*/
+class morkRowProbeMap : public morkProbeMap { // for mapping row IDs to rows
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseRowProbeMap() only if open
+  virtual ~morkRowProbeMap(); // assert CloseRowProbeMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkRowProbeMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots);
+  void CloseRowProbeMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsRowMap() const
+  { return IsNode() && mNode_Derived == morkDerived_kRowMap; }
+// } ===== end morkNode methods =====
+
+  // { ===== begin morkProbeMap methods =====
+  virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+  MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const;
+
+  virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+  MapHash(morkEnv* ev, const void* inAppKey) const;
+
+  virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const;
+
+  // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+
+  // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+  //   void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+
+  // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+  //   const void* inAppKey, const void* inAppVal, // (key,val) outside map
+  //   void* outMapKey, void* outMapVal);      // (key,val) inside map
+
+  // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+  //   const void* inMapKey, const void* inMapVal, // (key,val) inside map
+  //   void* outAppKey, void* outAppVal) const;    // (key,val) outside map
+  // } ===== end morkProbeMap methods =====
+
+public: // other map methods
+
+  mork_bool AddRow(morkEnv* ev, morkRow* ioRow);
+  // AddRow() returns ev->Good()
+
+  morkRow*  CutOid(morkEnv* ev, const mdbOid* inOid);
+  // CutRid() returns the row removed equal to inRid, if there was one
+
+  morkRow*  CutRow(morkEnv* ev, const morkRow* ioRow);
+  // CutRow() returns the row removed equal to ioRow, if there was one
+  
+  morkRow*  GetOid(morkEnv* ev, const mdbOid* inOid);
+  // GetOid() returns the row equal to inRid, or else nil
+  
+  morkRow*  GetRow(morkEnv* ev, const morkRow* ioRow);
+  // GetRow() returns the row equal to ioRow, or else nil
+  
+  // note the rows are owned elsewhere, usuall by morkRowSpace
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakRowProbeMap(morkRowProbeMap* me,
+    morkEnv* ev, morkRowProbeMap** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongRowProbeMap(morkRowProbeMap* me,
+    morkEnv* ev, morkRowProbeMap** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+class morkRowProbeMapIter: public morkProbeMapIter{ // typesafe wrapper class
+
+public:
+  morkRowProbeMapIter(morkEnv* ev, morkRowProbeMap* ioMap)
+  : morkProbeMapIter(ev, ioMap) { }
+ 
+  morkRowProbeMapIter( ) : morkProbeMapIter()  { }
+  void InitRowMapIter(morkEnv* ev, morkRowProbeMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change* FirstRow(morkEnv* ev, morkRow** outRowPtr)
+  { return this->First(ev, outRowPtr, /*val*/ (void*) 0); }
+  
+  mork_change* NextRow(morkEnv* ev, morkRow** outRowPtr)
+  { return this->Next(ev, outRowPtr, /*val*/ (void*) 0); }
+  
+  mork_change* HereRow(morkEnv* ev, morkRow** outRowPtr)
+  { return this->Here(ev, outRowPtr, /*val*/ (void*) 0); }
+  
+  mork_change* CutHereRow(morkEnv* ev, morkRow** outRowPtr)
+  { return this->CutHere(ev, outRowPtr, /*val*/ (void*) 0); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROWMAP_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRowObject.cpp
@@ -0,0 +1,660 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKROWCELLCURSOR_
+#include "morkRowCellCursor.h"
+#endif
+
+#ifndef _MORKCELLOBJECT_
+#include "morkCellObject.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowObject::CloseMorkNode(morkEnv* ev) // CloseRowObject() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseRowObject(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkRowObject::~morkRowObject() // assert CloseRowObject() executed earlier
+{
+  CloseMorkNode(mMorkEnv);
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowObject::morkRowObject(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+     morkRow* ioRow, morkStore* ioStore)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mRowObject_Row( 0 )
+, mRowObject_Store( 0 )
+{
+  if ( ev->Good() )
+  {
+    if ( ioRow && ioStore )
+    {
+      mRowObject_Row = ioRow;
+      mRowObject_Store = ioStore; // morkRowObjects don't ref-cnt the owning store.
+      
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kRowObject;
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkRowObject, morkObject, nsIMdbRow)
+// { ===== begin nsIMdbCollection methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkRowObject::GetSeed(nsIMdbEnv* mev,
+  mdb_seed* outSeed)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    *outSeed = (mdb_seed) mRowObject_Row->mRow_Seed;
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+NS_IMETHODIMP
+morkRowObject::GetCount(nsIMdbEnv* mev,
+  mdb_count* outCount)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    *outCount = (mdb_count) mRowObject_Row->mRow_Length;
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::GetPort(nsIMdbEnv* mev,
+  nsIMdbPort** acqPort)
+{
+  mdb_err outErr = 0;
+  nsIMdbPort* outPort = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRowSpace* rowSpace = mRowObject_Row->mRow_Space;
+    if ( rowSpace && rowSpace->mSpace_Store )
+    {
+      morkStore* store = mRowObject_Row->GetRowSpaceStore(ev);
+      if ( store )
+        outPort = store->AcquireStoreHandle(ev);
+    }
+    else
+      ev->NilPointerError();
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqPort )
+    *acqPort = outPort;
+    
+  return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin cursor methods -----
+NS_IMETHODIMP
+morkRowObject::GetCursor( // make a cursor starting iter at inMemberPos
+  nsIMdbEnv* mev, // context
+  mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+  nsIMdbCursor** acqCursor)
+{
+  return this->GetRowCellCursor(mev, inMemberPos,
+    (nsIMdbRowCellCursor**) acqCursor);
+}
+// } ----- end cursor methods -----
+
+// { ----- begin ID methods -----
+NS_IMETHODIMP
+morkRowObject::GetOid(nsIMdbEnv* mev,
+  mdbOid* outOid)
+{
+  *outOid = mRowObject_Row->mRow_Oid;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  return (ev) ? ev->AsErr() : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+morkRowObject::BecomeContent(nsIMdbEnv* mev,
+  const mdbOid* inOid)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+  // remember row->MaybeDirtySpaceStoreAndRow();
+}
+// } ----- end ID methods -----
+
+// { ----- begin activity dropping methods -----
+NS_IMETHODIMP
+morkRowObject::DropActivity( // tell collection usage no longer expected
+  nsIMdbEnv* mev)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+
+// { ===== begin nsIMdbRow methods =====
+
+// { ----- begin cursor methods -----
+NS_IMETHODIMP
+morkRowObject::GetRowCellCursor( // make a cursor starting iteration at inCellPos
+  nsIMdbEnv* mev, // context
+  mdb_pos inPos, // zero-based ordinal position of cell in row
+  nsIMdbRowCellCursor** acqCursor)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  nsIMdbRowCellCursor* outCursor = 0;
+  if ( ev )
+  {
+    morkRowCellCursor* cursor = mRowObject_Row->NewRowCellCursor(ev, inPos);
+    if ( cursor )
+    {
+      if ( ev->Good() )
+      {
+        cursor->mCursor_Seed = (mork_seed) inPos;
+        outCursor = cursor;
+        NS_ADDREF(cursor);
+      }
+    }
+    outErr = ev->AsErr();
+  }
+  if ( acqCursor )
+    *acqCursor = outCursor;
+  return outErr;
+}
+// } ----- end cursor methods -----
+
+// { ----- begin column methods -----
+NS_IMETHODIMP
+morkRowObject::AddColumn( // make sure a particular column is inside row
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // column to add
+  const mdbYarn* inYarn)
+{
+  mdb_err outErr = NS_ERROR_FAILURE;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( mRowObject_Store && mRowObject_Row)
+      mRowObject_Row->AddColumn(ev, inColumn, inYarn, mRowObject_Store);
+      
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::CutColumn( // make sure a column is absent from the row
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn)
+{
+  mdb_err outErr = NS_ERROR_FAILURE;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    mRowObject_Row->CutColumn(ev, inColumn);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::CutAllColumns( // remove all columns from the row
+  nsIMdbEnv* mev)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    mRowObject_Row->CutAllColumns(ev);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ----- end column methods -----
+
+// { ----- begin cell methods -----
+NS_IMETHODIMP
+morkRowObject::NewCell( // get cell for specified column, or add new one
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // column to add
+  nsIMdbCell** acqCell)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    GetCell(mev, inColumn, acqCell);
+    if ( !*acqCell )
+    {
+      if ( mRowObject_Store )
+      {
+        mdbYarn yarn; // to pass empty yarn into morkRowObject::AddColumn()
+        yarn.mYarn_Buf = 0;
+        yarn.mYarn_Fill = 0;
+        yarn.mYarn_Size = 0;
+        yarn.mYarn_More = 0;
+        yarn.mYarn_Form = 0;
+        yarn.mYarn_Grow = 0;
+        AddColumn(ev, inColumn, &yarn);
+        GetCell(mev, inColumn, acqCell);
+      }
+    }
+      
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkRowObject::AddCell( // copy a cell from another row to this row
+  nsIMdbEnv* mev, // context
+  const nsIMdbCell* inCell)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkCell* cell = 0;
+    morkCellObject* cellObj = (morkCellObject*) inCell;
+    if ( cellObj->CanUseCell(mev, morkBool_kFalse, &outErr, &cell) )
+    {
+
+      morkRow* cellRow = cellObj->mCellObject_Row;
+      if ( cellRow )
+      {
+        if ( mRowObject_Row != cellRow )
+        {
+          morkStore* store = mRowObject_Row->GetRowSpaceStore(ev);
+          morkStore* cellStore = cellRow->GetRowSpaceStore(ev);
+          if ( store && cellStore )
+          {
+            mork_column col = cell->GetColumn();
+            morkAtom* atom = cell->mCell_Atom;
+            mdbYarn yarn;
+            atom->AliasYarn(&yarn); // works even when atom is nil
+            
+            if ( store != cellStore )
+              col = store->CopyToken(ev, col, cellStore);
+            if ( ev->Good() )
+              AddColumn(ev, col, &yarn);
+          }
+          else
+            ev->NilPointerError();
+        }
+      }
+      else
+        ev->NilPointerError();
+    }
+
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkRowObject::GetCell( // find a cell in this row
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // column to find
+  nsIMdbCell** acqCell)
+{
+  mdb_err outErr = 0;
+  nsIMdbCell* outCell = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+
+  if ( ev )
+  {
+    if ( inColumn )
+    {
+      mork_pos pos = 0;
+      morkCell* cell = mRowObject_Row->GetCell(ev, inColumn, &pos);
+      if ( cell )
+      {
+        outCell = mRowObject_Row->AcquireCellHandle(ev, cell, inColumn, pos);
+      }
+    }
+    else
+      mRowObject_Row->ZeroColumnError(ev);
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqCell )
+    *acqCell = outCell;
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkRowObject::EmptyAllCells( // make all cells in row empty of content
+  nsIMdbEnv* mev)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    EmptyAllCells(ev);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ----- end cell methods -----
+
+// { ----- begin row methods -----
+NS_IMETHODIMP
+morkRowObject::AddRow( // add all cells in another row to this one
+  nsIMdbEnv* mev, // context
+  nsIMdbRow* ioSourceRow)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRow* unsafeSource = (morkRow*) ioSourceRow; // unsafe cast
+//    if ( unsafeSource->CanUseRow(mev, morkBool_kFalse, &outErr, &source) )
+    {
+      mRowObject_Row->AddRow(ev, unsafeSource);
+    }
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkRowObject::SetRow( // make exact duplicate of another row
+  nsIMdbEnv* mev, // context
+  nsIMdbRow* ioSourceRow)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRowObject *sourceObject = (morkRowObject *) ioSourceRow; // unsafe cast
+    morkRow* unsafeSource = sourceObject->mRowObject_Row;
+//    if ( unsafeSource->CanUseRow(mev, morkBool_kFalse, &outErr, &source) )
+    {
+      mRowObject_Row->SetRow(ev, unsafeSource);
+    }
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ----- end row methods -----
+
+// { ----- begin blob methods -----
+NS_IMETHODIMP
+morkRowObject::SetCellYarn( // synonym for AddColumn()
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // column to add
+  const mdbYarn* inYarn)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( mRowObject_Store )
+      AddColumn(ev, inColumn, inYarn);
+      
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+NS_IMETHODIMP
+morkRowObject::GetCellYarn(
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // column to read 
+  mdbYarn* outYarn)  // writes some yarn slots 
+// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( mRowObject_Store && mRowObject_Row)
+    {
+      morkAtom* atom = mRowObject_Row->GetColumnAtom(ev, inColumn);
+      atom->GetYarn(outYarn);
+      // note nil atom works and sets yarn correctly
+    }
+      
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::AliasCellYarn(
+  nsIMdbEnv* mev, // context
+    mdb_column inColumn, // column to alias
+    mdbYarn* outYarn) // writes ALL yarn slots
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( mRowObject_Store && mRowObject_Row)
+    {
+      morkAtom* atom = mRowObject_Row->GetColumnAtom(ev, inColumn);
+      atom->AliasYarn(outYarn);
+      // note nil atom works and sets yarn correctly
+    }
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::NextCellYarn(nsIMdbEnv* mev, // iterative version of GetCellYarn()
+  mdb_column* ioColumn, // next column to read
+  mdbYarn* outYarn)  // writes some yarn slots 
+// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+//
+// The ioColumn argument is an inout parameter which initially contains the
+// last column accessed and returns the next column corresponding to the
+// content read into the yarn.  Callers should start with a zero column
+// value to say 'no previous column', which causes the first column to be
+// read.  Then the value returned in ioColumn is perfect for the next call
+// to NextCellYarn(), since it will then be the previous column accessed.
+// Callers need only examine the column token returned to see which cell
+// in the row is being read into the yarn.  When no more columns remain,
+// and the iteration has ended, ioColumn will return a zero token again.
+// So iterating over cells starts and ends with a zero column token.
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( mRowObject_Store && mRowObject_Row)
+      mRowObject_Row->NextColumn(ev, ioColumn, outYarn);
+      
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell()
+  nsIMdbEnv* mev, // context
+  mdb_pos inPos, // position of cell in row sequence
+  mdb_column* outColumn, // column for this particular cell
+  mdbYarn* outYarn) // writes some yarn slots
+// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+// Callers can pass nil for outYarn to indicate no interest in content, so
+// only the outColumn value is returned.  NOTE to subclasses: you must be
+// able to ignore outYarn when the pointer is nil; please do not crash.
+
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( mRowObject_Store && mRowObject_Row)
+      mRowObject_Row->SeekColumn(ev, inPos, outColumn, outYarn);
+      
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+// } ----- end blob methods -----
+
+
+// } ===== end nsIMdbRow methods =====
+
+
+
+/*public non-poly*/ void
+morkRowObject::CloseRowObject(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      morkRow* row = mRowObject_Row;
+      mRowObject_Row = 0;
+      this->CloseObject(ev);
+      this->MarkShut();
+
+      if ( row )
+      {
+        MORK_ASSERT(row->mRow_Object == this);
+        if ( row->mRow_Object == this )
+        {
+          row->mRow_Object = 0; // just nil this slot -- cut ref down below
+          
+          mRowObject_Store = 0; // morkRowObjects don't ref-cnt the owning store.
+            
+          this->CutWeakRef(ev->AsMdbEnv()); // do last, because it might self destroy
+        }
+      }
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkRowObject::NonRowObjectTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkRowObject");
+}
+
+/*static*/ void
+morkRowObject::NilRowError(morkEnv* ev)
+{
+  ev->NewError("nil mRowObject_Row");
+}
+
+/*static*/ void
+morkRowObject::NilStoreError(morkEnv* ev)
+{
+  ev->NewError("nil mRowObject_Store");
+}
+
+/*static*/ void
+morkRowObject::RowObjectRowNotSelfError(morkEnv* ev)
+{
+  ev->NewError("mRowObject_Row->mRow_Object != self");
+}
+
+
+nsIMdbRow*
+morkRowObject::AcquireRowHandle(morkEnv* ev) // mObject_Handle
+{
+  AddRef();
+  return this;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRowObject.h
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKROWOBJECT_
+#define _MORKROWOBJECT_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class nsIMdbRow;
+#define morkDerived_kRowObject  /*i*/ 0x724F /* ascii 'rO' */
+
+class morkRowObject : public morkObject, public nsIMdbRow  { //
+
+public: // state is public because the entire Mork system is private
+  NS_DECL_ISUPPORTS_INHERITED
+  
+  morkRow*    mRowObject_Row;     // non-refcounted alias to morkRow
+  morkStore*  mRowObject_Store;   // non-refcounted ptr to store containing row
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseRowObject() only if open
+  virtual ~morkRowObject(); // assert that CloseRowObject() executed earlier
+  
+public: // morkRowObject construction & destruction
+  morkRowObject(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkRow* ioRow, morkStore* ioStore);
+  void CloseRowObject(morkEnv* ev); // called by CloseMorkNode();
+
+// { ===== begin nsIMdbCollection methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetSeed(nsIMdbEnv* ev,
+    mdb_seed* outSeed);    // member change count
+  NS_IMETHOD GetCount(nsIMdbEnv* ev,
+    mdb_count* outCount); // member count
+
+  NS_IMETHOD GetPort(nsIMdbEnv* ev,
+    nsIMdbPort** acqPort); // collection container
+  // } ----- end attribute methods -----
+
+  // { ----- begin cursor methods -----
+  NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos
+    nsIMdbEnv* ev, // context
+    mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+    nsIMdbCursor** acqCursor); // acquire new cursor instance
+  // } ----- end cursor methods -----
+
+  // { ----- begin ID methods -----
+  NS_IMETHOD GetOid(nsIMdbEnv* ev,
+    mdbOid* outOid); // read object identity
+  NS_IMETHOD BecomeContent(nsIMdbEnv* ev,
+    const mdbOid* inOid); // exchange content
+  // } ----- end ID methods -----
+
+  // { ----- begin activity dropping methods -----
+  NS_IMETHOD DropActivity( // tell collection usage no longer expected
+    nsIMdbEnv* ev);
+  // } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+// { ===== begin nsIMdbRow methods =====
+
+  // { ----- begin cursor methods -----
+  NS_IMETHOD GetRowCellCursor( // make a cursor starting iteration at inRowPos
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    nsIMdbRowCellCursor** acqCursor); // acquire new cursor instance
+  // } ----- end cursor methods -----
+
+  // { ----- begin column methods -----
+  NS_IMETHOD AddColumn( // make sure a particular column is inside row
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // column to add
+    const mdbYarn* inYarn); // cell value to install
+
+  NS_IMETHOD CutColumn( // make sure a column is absent from the row
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn); // column to ensure absent from row
+
+  NS_IMETHOD CutAllColumns( // remove all columns from the row
+    nsIMdbEnv* ev); // context
+  // } ----- end column methods -----
+
+  // { ----- begin cell methods -----
+  NS_IMETHOD NewCell( // get cell for specified column, or add new one
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // column to add
+    nsIMdbCell** acqCell); // cell column and value
+    
+  NS_IMETHOD AddCell( // copy a cell from another row to this row
+    nsIMdbEnv* ev, // context
+    const nsIMdbCell* inCell); // cell column and value
+    
+  NS_IMETHOD GetCell( // find a cell in this row
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // column to find
+    nsIMdbCell** acqCell); // cell for specified column, or null
+    
+  NS_IMETHOD EmptyAllCells( // make all cells in row empty of content
+    nsIMdbEnv* ev); // context
+  // } ----- end cell methods -----
+
+  // { ----- begin row methods -----
+  NS_IMETHOD AddRow( // add all cells in another row to this one
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioSourceRow); // row to union with
+    
+  NS_IMETHOD SetRow( // make exact duplicate of another row
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioSourceRow); // row to duplicate
+  // } ----- end row methods -----
+
+  // { ----- begin blob methods -----  
+  NS_IMETHOD SetCellYarn(nsIMdbEnv* ev, // synonym for AddColumn()
+    mdb_column inColumn, // column to write
+    const mdbYarn* inYarn);   // reads from yarn slots
+  // make this text object contain content from the yarn's buffer
+  
+  NS_IMETHOD GetCellYarn(nsIMdbEnv* ev, 
+    mdb_column inColumn, // column to read 
+    mdbYarn* outYarn);  // writes some yarn slots 
+  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+  
+  NS_IMETHOD AliasCellYarn(nsIMdbEnv* ev, 
+    mdb_column inColumn, // column to alias
+    mdbYarn* outYarn); // writes ALL yarn slots
+  
+  NS_IMETHOD NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn()
+    mdb_column* ioColumn, // next column to read
+    mdbYarn* outYarn);  // writes some yarn slots 
+  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+  //
+  // The ioColumn argument is an inout parameter which initially contains the
+  // last column accessed and returns the next column corresponding to the
+  // content read into the yarn.  Callers should start with a zero column
+  // value to say 'no previous column', which causes the first column to be
+  // read.  Then the value returned in ioColumn is perfect for the next call
+  // to NextCellYarn(), since it will then be the previous column accessed.
+  // Callers need only examine the column token returned to see which cell
+  // in the row is being read into the yarn.  When no more columns remain,
+  // and the iteration has ended, ioColumn will return a zero token again.
+  // So iterating over cells starts and ends with a zero column token.
+
+  NS_IMETHOD SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell()
+    nsIMdbEnv* ev, // context
+    mdb_pos inPos, // position of cell in row sequence
+    mdb_column* outColumn, // column for this particular cell
+    mdbYarn* outYarn); // writes some yarn slots
+  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+  // Callers can pass nil for outYarn to indicate no interest in content, so
+  // only the outColumn value is returned.  NOTE to subclasses: you must be
+  // able to ignore outYarn when the pointer is nil; please do not crash.
+
+  // } ----- end blob methods -----
+
+// } ===== end nsIMdbRow methods =====
+
+private: // copying is not allowed
+  morkRowObject(const morkRowObject& other);
+  morkRowObject& operator=(const morkRowObject& other);
+
+public: // dynamic type identification
+  mork_bool IsRowObject() const
+  { return IsNode() && mNode_Derived == morkDerived_kRowObject; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonRowObjectTypeError(morkEnv* ev);
+  static void NilRowError(morkEnv* ev);
+  static void NilStoreError(morkEnv* ev);
+  static void RowObjectRowNotSelfError(morkEnv* ev);
+
+public: // other row node methods
+
+  nsIMdbRow* AcquireRowHandle(morkEnv* ev); // mObject_Handle
+  
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakRowObject(morkRowObject* me,
+    morkEnv* ev, morkRowObject** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongRowObject(morkRowObject* me,
+    morkEnv* ev, morkRowObject** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROWOBJECT_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRowSpace.cpp
@@ -0,0 +1,666 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowSpace::CloseMorkNode(morkEnv* ev) // CloseRowSpace() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseRowSpace(ev);
+    this->MarkShut();  
+  }
+}
+
+/*public virtual*/
+morkRowSpace::~morkRowSpace() // assert CloseRowSpace() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowSpace::morkRowSpace(morkEnv* ev, 
+  const morkUsage& inUsage, mork_scope inScope, morkStore* ioStore,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap)
+, mRowSpace_SlotHeap( ioSlotHeap )
+, mRowSpace_Rows(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap,
+  morkRowSpace_kStartRowMapSlotCount)
+, mRowSpace_Tables(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
+, mRowSpace_NextTableId( 1 )
+, mRowSpace_NextRowId( 1 )
+
+, mRowSpace_IndexCount( 0 )
+{
+  morkAtomRowMap** cache = mRowSpace_IndexCache;
+  morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize;
+  while ( cache < cacheEnd )
+    *cache++ = 0; // put nil into every slot of cache table
+    
+  if ( ev->Good() )
+  {
+    if ( ioSlotHeap )
+    {
+      mNode_Derived = morkDerived_kRowSpace;
+      
+      // the morkSpace base constructor handles any dirty propagation
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+/*public non-poly*/ void
+morkRowSpace::CloseRowSpace(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      morkAtomRowMap** cache = mRowSpace_IndexCache;
+      morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize;
+      --cache; // prepare for preincrement:
+      while ( ++cache < cacheEnd )
+      {
+        if ( *cache )
+          morkAtomRowMap::SlotStrongAtomRowMap(0, ev, cache);
+      }
+      
+      mRowSpace_Tables.CloseMorkNode(ev);
+      
+      morkStore* store = mSpace_Store;
+      if ( store )
+        this->CutAllRows(ev, &store->mStore_Pool);
+      
+      mRowSpace_Rows.CloseMorkNode(ev);
+      this->CloseSpace(ev);
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkRowSpace::NonRowSpaceTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkRowSpace");
+}
+
+/*static*/ void
+morkRowSpace::ZeroKindError(morkEnv* ev)
+{
+  ev->NewError("zero table kind");
+}
+
+/*static*/ void
+morkRowSpace::ZeroScopeError(morkEnv* ev)
+{
+  ev->NewError("zero row scope");
+}
+
+/*static*/ void
+morkRowSpace::ZeroTidError(morkEnv* ev)
+{
+  ev->NewError("zero table ID");
+}
+
+/*static*/ void
+morkRowSpace::MinusOneRidError(morkEnv* ev)
+{
+  ev->NewError("row ID is -1");
+}
+
+///*static*/ void
+//morkRowSpace::ExpectAutoIdOnlyError(morkEnv* ev)
+//{
+//  ev->NewError("zero row ID");
+//}
+
+///*static*/ void
+//morkRowSpace::ExpectAutoIdNeverError(morkEnv* ev)
+//{
+//}
+
+mork_num
+morkRowSpace::CutAllRows(morkEnv* ev, morkPool* ioPool)
+{
+  if ( this->IsRowSpaceClean() )
+    this->MaybeDirtyStoreAndSpace();
+  
+  mork_num outSlots = mRowSpace_Rows.MapFill();
+
+#ifdef MORK_ENABLE_ZONE_ARENAS
+  MORK_USED_2(ev, ioPool);
+  return 0;
+#else /*MORK_ENABLE_ZONE_ARENAS*/
+  morkZone* zone = &mSpace_Store->mStore_Zone;
+  morkRow* r = 0; // old key row in the map
+  mork_change* c = 0;
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+  morkRowProbeMapIter i(ev, &mRowSpace_Rows);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  morkRowMapIter i(ev, &mRowSpace_Rows);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  
+  for ( c = i.FirstRow(ev, &r); c && ev->Good();
+        c = i.NextRow(ev, &r) )
+  {
+    if ( r )
+    {
+      if ( r->IsRow() )
+      {
+        if ( r->mRow_Object )
+        {
+          morkRowObject::SlotWeakRowObject((morkRowObject*) 0, ev,
+            &r->mRow_Object);
+        }
+        ioPool->ZapRow(ev, r, zone);
+      }
+      else
+        r->NonRowTypeWarning(ev);
+    }
+    else
+      ev->NilPointerError();
+    
+#ifdef MORK_ENABLE_PROBE_MAPS
+    // cut nothing from the map
+#else /*MORK_ENABLE_PROBE_MAPS*/
+    i.CutHereRow(ev, /*key*/ (morkRow**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  }
+#endif /*MORK_ENABLE_ZONE_ARENAS*/
+  
+  
+  return outSlots;
+}
+
+morkTable*
+morkRowSpace::FindTableByKind(morkEnv* ev, mork_kind inTableKind)
+{
+  if ( inTableKind )
+  {
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+
+    morkTableMapIter i(ev, &mRowSpace_Tables);
+    morkTable* table = i.FirstTable(ev);
+    for ( ; table && ev->Good(); table = i.NextTable(ev) )
+          
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+    mork_tid* key = 0; // nil pointer to suppress key access
+    morkTable* table = 0; // old table in the map
+
+    mork_change* c = 0;
+    morkTableMapIter i(ev, &mRowSpace_Tables);
+    for ( c = i.FirstTable(ev, key, &table); c && ev->Good();
+          c = i.NextTable(ev, key, &table) )
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+    {
+      if ( table->mTable_Kind == inTableKind )
+        return table;
+    }
+  }
+  else
+    this->ZeroKindError(ev);
+    
+  return (morkTable*) 0;
+}
+
+morkTable*
+morkRowSpace::NewTableWithTid(morkEnv* ev, mork_tid inTid,
+  mork_kind inTableKind,
+  const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying 
+{
+  morkTable* outTable = 0;
+  morkStore* store = mSpace_Store;
+  
+  if ( inTableKind && store )
+  {
+    mdb_bool mustBeUnique = morkBool_kFalse;
+    nsIMdbHeap* heap = store->mPort_Heap;
+    morkTable* table = new(*heap, ev)
+      morkTable(ev, morkUsage::kHeap, heap, store, heap, this,
+        inOptionalMetaRowOid, inTid, inTableKind, mustBeUnique);
+    if ( table )
+    {
+      if ( mRowSpace_Tables.AddTable(ev, table) )
+      {
+        outTable = table;
+        if ( mRowSpace_NextTableId <= inTid )
+          mRowSpace_NextTableId = inTid + 1;
+      }
+        
+      if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
+        this->MaybeDirtyStoreAndSpace(); // morkTable does already
+
+    }
+  }
+  else if ( store )
+    this->ZeroKindError(ev);
+  else
+    this->NilSpaceStoreError(ev);
+    
+  return outTable;
+}
+
+morkTable*
+morkRowSpace::NewTable(morkEnv* ev, mork_kind inTableKind,
+  mdb_bool inMustBeUnique,
+  const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying 
+{
+  morkTable* outTable = 0;
+  morkStore* store = mSpace_Store;
+  
+  if ( inTableKind && store )
+  {
+    if ( inMustBeUnique ) // need to look for existing table first?
+      outTable = this->FindTableByKind(ev, inTableKind);
+      
+    if ( !outTable && ev->Good() )
+    {
+      mork_tid id = this->MakeNewTableId(ev);
+      if ( id )
+      {
+        nsIMdbHeap* heap = mSpace_Store->mPort_Heap;
+        morkTable* table = new(*heap, ev)
+          morkTable(ev, morkUsage::kHeap, heap, mSpace_Store, heap, this,
+            inOptionalMetaRowOid, id, inTableKind, inMustBeUnique);
+        if ( table )
+        {
+          if ( mRowSpace_Tables.AddTable(ev, table) )
+            outTable = table;
+          else
+            table->Release();
+
+          if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
+            this->MaybeDirtyStoreAndSpace(); // morkTable does already
+        }
+      }
+    }
+  }
+  else if ( store )
+    this->ZeroKindError(ev);
+  else
+    this->NilSpaceStoreError(ev);
+    
+  return outTable;
+}
+
+mork_tid
+morkRowSpace::MakeNewTableId(morkEnv* ev)
+{
+  mork_tid outTid = 0;
+  mork_tid id = mRowSpace_NextTableId;
+  mork_num count = 9; // try up to eight times
+  
+  while ( !outTid && --count ) // still trying to find an unused table ID?
+  {
+    if ( !mRowSpace_Tables.GetTable(ev, id) )
+      outTid = id;
+    else
+    {
+      MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems
+      ++id;
+    }
+  }
+  
+  mRowSpace_NextTableId = id + 1;
+  return outTid;
+}
+
+mork_rid
+morkRowSpace::MakeNewRowId(morkEnv* ev)
+{
+  mork_rid outRid = 0;
+  mork_rid id = mRowSpace_NextRowId;
+  mork_num count = 9; // try up to eight times
+  mdbOid oid;
+  oid.mOid_Scope = this->SpaceScope();
+  
+  while ( !outRid && --count ) // still trying to find an unused row ID?
+  {
+    oid.mOid_Id = id;
+    if ( !mRowSpace_Rows.GetOid(ev, &oid) )
+      outRid = id;
+    else
+    {
+      MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems
+      ++id;
+    }
+  }
+  
+  mRowSpace_NextRowId = id + 1;
+  return outRid;
+}
+
+morkAtomRowMap*
+morkRowSpace::make_index(morkEnv* ev, mork_column inCol)
+{
+  morkAtomRowMap* outMap = 0;
+  nsIMdbHeap* heap = mRowSpace_SlotHeap;
+  if ( heap ) // have expected heap for allocations?
+  {
+    morkAtomRowMap* map = new(*heap, ev)
+      morkAtomRowMap(ev, morkUsage::kHeap, heap, heap, inCol);
+    
+    if ( map ) // able to create new map index?
+    {
+      if ( ev->Good() ) // no errors during construction?
+      {
+#ifdef MORK_ENABLE_PROBE_MAPS
+        morkRowProbeMapIter i(ev, &mRowSpace_Rows);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+        morkRowMapIter i(ev, &mRowSpace_Rows);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+        mork_change* c = 0;
+        morkRow* row = 0;
+        mork_aid aidKey = 0;
+        
+        for ( c = i.FirstRow(ev, &row); c && ev->Good();
+              c = i.NextRow(ev, &row) ) // another row in space?
+        {
+          aidKey = row->GetCellAtomAid(ev, inCol);
+          if ( aidKey ) // row has indexed attribute?
+            map->AddAid(ev, aidKey, row); // include in map
+        }
+      }
+      if ( ev->Good() ) // no errors constructing index?
+        outMap = map; // return from function
+      else
+        map->CutStrongRef(ev); // discard map on error
+    }
+  }
+  else
+    ev->NilPointerError();
+  
+  return outMap;
+}
+
+morkAtomRowMap*
+morkRowSpace::ForceMap(morkEnv* ev, mork_column inCol)
+{
+  morkAtomRowMap* outMap = this->FindMap(ev, inCol);
+  
+  if ( !outMap && ev->Good() ) // no such existing index?
+  {
+    if ( mRowSpace_IndexCount < morkRowSpace_kMaxIndexCount )
+    {
+      morkAtomRowMap* map = this->make_index(ev, inCol);
+      if ( map ) // created a new index for col?
+      {
+        mork_count wrap = 0; // count times wrap-around occurs
+        morkAtomRowMap** slot = mRowSpace_IndexCache; // table
+        morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize;
+        slot += ( inCol % morkRowSpace_kPrimeCacheSize ); // hash
+        while ( *slot ) // empty slot not yet found?
+        {
+          if ( ++slot >= end ) // wrap around?
+          {
+            slot = mRowSpace_IndexCache; // back to table start
+            if ( ++wrap > 1 ) // wrapped more than once?
+            {
+              ev->NewError("no free cache slots"); // disaster
+              break; // end while loop
+            }
+          }
+        }
+        if ( ev->Good() ) // everything went just fine?
+        {
+          ++mRowSpace_IndexCount; // note another new map
+          *slot = map; // install map in the hash table
+          outMap = map; // return the new map from function
+        }
+        else
+          map->CutStrongRef(ev); // discard map on error
+      }
+    }
+    else
+      ev->NewError("too many indexes"); // why so many indexes?
+  }
+  return outMap;
+}
+
+morkAtomRowMap*
+morkRowSpace::FindMap(morkEnv* ev, mork_column inCol)
+{
+  if ( mRowSpace_IndexCount && ev->Good() )
+  {
+    mork_count wrap = 0; // count times wrap-around occurs
+    morkAtomRowMap** slot = mRowSpace_IndexCache; // table
+    morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize;
+    slot += ( inCol % morkRowSpace_kPrimeCacheSize ); // hash
+    morkAtomRowMap* map = *slot;
+    while ( map ) // another used slot to examine?
+    {
+      if ( inCol == map->mAtomRowMap_IndexColumn ) // found col?
+        return map;
+      if ( ++slot >= end ) // wrap around?
+      {
+        slot = mRowSpace_IndexCache;
+        if ( ++wrap > 1 ) // wrapped more than once?
+          return (morkAtomRowMap*) 0; // stop searching
+      }
+      map = *slot;
+    }
+  }
+  return (morkAtomRowMap*) 0;
+}
+
+morkRow*
+morkRowSpace::FindRow(morkEnv* ev, mork_column inCol, const mdbYarn* inYarn)
+{
+  morkRow* outRow = 0;
+
+  // if yarn hasn't been atomized, there can't be a corresponding row,
+  // so pass in PR_FALSE to not create the row - should help history bloat
+  morkAtom* atom = mSpace_Store->YarnToAtom(ev, inYarn, PR_FALSE);
+  if ( atom ) // have or created an atom corresponding to input yarn?
+  {
+    mork_aid atomAid = atom->GetBookAtomAid();
+    if ( atomAid ) // atom has an identity for use in hash table?
+    {
+      morkAtomRowMap* map = this->ForceMap(ev, inCol);
+      if ( map ) // able to find or create index for col?
+      {
+        outRow = map->GetAid(ev, atomAid); // search for row
+      }
+    }
+  }
+  
+  return outRow;
+}
+
+morkRow*
+morkRowSpace::NewRowWithOid(morkEnv* ev, const mdbOid* inOid)
+{
+  morkRow* outRow = mRowSpace_Rows.GetOid(ev, inOid);
+  MORK_ASSERT(outRow==0);
+  if ( !outRow && ev->Good() )
+  {
+    morkStore* store = mSpace_Store;
+    if ( store )
+    {
+      morkPool* pool = this->GetSpaceStorePool();
+      morkRow* row = pool->NewRow(ev, &store->mStore_Zone);
+      if ( row )
+      {
+        row->InitRow(ev, inOid, this, /*length*/ 0, pool);
+        
+        if ( ev->Good() && mRowSpace_Rows.AddRow(ev, row) )
+        {
+          outRow = row;
+          mork_rid rid = inOid->mOid_Id;
+          if ( mRowSpace_NextRowId <= rid )
+            mRowSpace_NextRowId = rid + 1;
+        }
+        else
+          pool->ZapRow(ev, row, &store->mStore_Zone);
+
+        if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
+          this->MaybeDirtyStoreAndSpace(); // InitRow() does already
+      }
+    }
+    else
+      this->NilSpaceStoreError(ev);
+  }
+  return outRow;
+}
+
+morkRow*
+morkRowSpace::NewRow(morkEnv* ev)
+{
+  morkRow* outRow = 0;
+  if ( ev->Good() )
+  {
+    mork_rid id = this->MakeNewRowId(ev);
+    if ( id )
+    {
+      morkStore* store = mSpace_Store;
+      if ( store )
+      {
+        mdbOid oid;
+        oid.mOid_Scope = this->SpaceScope();
+        oid.mOid_Id = id;
+        morkPool* pool = this->GetSpaceStorePool();
+        morkRow* row = pool->NewRow(ev, &store->mStore_Zone);
+        if ( row )
+        {
+          row->InitRow(ev, &oid, this, /*length*/ 0, pool);
+          
+          if ( ev->Good() && mRowSpace_Rows.AddRow(ev, row) )
+            outRow = row;
+          else
+            pool->ZapRow(ev, row, &store->mStore_Zone);
+
+          if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
+            this->MaybeDirtyStoreAndSpace(); // InitRow() does already
+        }
+      }
+      else
+        this->NilSpaceStoreError(ev);
+    }
+  }
+  return outRow;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+morkRowSpaceMap::~morkRowSpaceMap()
+{
+}
+
+morkRowSpaceMap::morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+  : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kRowSpaceMap;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkRowSpace.h
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKROWSPACE_
+#define _MORKROWSPACE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkArray.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kRowSpace  /*i*/ 0x7253 /* ascii 'rS' */
+
+#define morkRowSpace_kStartRowMapSlotCount 11
+
+#define morkRowSpace_kMaxIndexCount 8 /* no more indexes than this */
+#define morkRowSpace_kPrimeCacheSize 17 /* should be prime number */
+
+class morkAtomRowMap;
+
+/*| morkRowSpace:
+|*/
+class morkRowSpace : public morkSpace { // 
+
+// public: // slots inherited from morkSpace (meant to inform only)
+  // nsIMdbHeap*    mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+  
+  // morkStore*  mSpace_Store; // weak ref to containing store
+  
+  // mork_bool   mSpace_DoAutoIDs;    // whether db should assign member IDs
+  // mork_bool   mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs
+  // mork_u1     mSpace_Pad[ 2 ];    // pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+
+  nsIMdbHeap*  mRowSpace_SlotHeap;
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+  morkRowProbeMap   mRowSpace_Rows;   // hash table of morkRow instances
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  morkRowMap   mRowSpace_Rows;   // hash table of morkRow instances
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+  morkTableMap mRowSpace_Tables; // all the tables in this row scope
+
+  mork_tid     mRowSpace_NextTableId;  // for auto-assigning table IDs
+  mork_rid     mRowSpace_NextRowId;    // for auto-assigning row IDs
+  
+  mork_count   mRowSpace_IndexCount; // if nonzero, row indexes exist
+    
+  // every nonzero slot in IndexCache is a strong ref to a morkAtomRowMap:
+  morkAtomRowMap* mRowSpace_IndexCache[ morkRowSpace_kPrimeCacheSize ];
+
+  morkDeque    mRowSpace_TablesByPriority[ morkPriority_kCount ];
+
+public: // more specific dirty methods for row space:
+  void SetRowSpaceDirty() { this->SetNodeDirty(); }
+  void SetRowSpaceClean() { this->SetNodeClean(); }
+  
+  mork_bool IsRowSpaceClean() const { return this->IsNodeClean(); }
+  mork_bool IsRowSpaceDirty() const { return this->IsNodeDirty(); }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseRowSpace() only if open
+  virtual ~morkRowSpace(); // assert that CloseRowSpace() executed earlier
+  
+public: // morkMap construction & destruction
+  morkRowSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope,
+    morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseRowSpace(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsRowSpace() const
+  { return IsNode() && mNode_Derived == morkDerived_kRowSpace; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonRowSpaceTypeError(morkEnv* ev);
+  static void ZeroScopeError(morkEnv* ev);
+  static void ZeroKindError(morkEnv* ev);
+  static void ZeroTidError(morkEnv* ev);
+  static void MinusOneRidError(morkEnv* ev);
+
+  //static void ExpectAutoIdOnlyError(morkEnv* ev);
+  //static void ExpectAutoIdNeverError(morkEnv* ev);
+
+public: // other space methods
+
+  mork_num CutAllRows(morkEnv* ev, morkPool* ioPool);
+  // CutAllRows() puts all rows and cells back into the pool.
+  
+  morkTable* NewTable(morkEnv* ev, mork_kind inTableKind,
+    mdb_bool inMustBeUnique, const mdbOid* inOptionalMetaRowOid);
+  
+  morkTable* NewTableWithTid(morkEnv* ev, mork_tid inTid,
+    mork_kind inTableKind, const mdbOid* inOptionalMetaRowOid);
+  
+  morkTable* FindTableByKind(morkEnv* ev, mork_kind inTableKind);
+  morkTable* FindTableByTid(morkEnv* ev, mork_tid inTid)
+  { return mRowSpace_Tables.GetTable(ev, inTid); }
+
+  mork_tid MakeNewTableId(morkEnv* ev);
+  mork_rid MakeNewRowId(morkEnv* ev);
+
+  // morkRow* FindRowByRid(morkEnv* ev, mork_rid inRid)
+  // { return (morkRow*) mRowSpace_Rows.GetRow(ev, inRid); }
+
+  morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid);
+  morkRow* NewRow(morkEnv* ev);
+
+  morkRow* FindRow(morkEnv* ev, mork_column inColumn, const mdbYarn* inYarn);
+
+  morkAtomRowMap* ForceMap(morkEnv* ev, mork_column inColumn);
+  morkAtomRowMap* FindMap(morkEnv* ev, mork_column inColumn);
+
+protected: // internal utilities
+  morkAtomRowMap* make_index(morkEnv* ev, mork_column inColumn);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakRowSpace(morkRowSpace* me,
+    morkEnv* ev, morkRowSpace** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongRowSpace(morkRowSpace* me,
+    morkEnv* ev, morkRowSpace** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kRowSpaceMap  /*i*/ 0x725A /* ascii 'rZ' */
+
+/*| morkRowSpaceMap: maps mork_scope -> morkRowSpace
+|*/
+class morkRowSpaceMap : public morkNodeMap { // for mapping tokens to tables
+
+public:
+
+  virtual ~morkRowSpaceMap();
+  morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+
+public: // other map methods
+
+  mork_bool  AddRowSpace(morkEnv* ev, morkRowSpace* ioRowSpace)
+  { return this->AddNode(ev, ioRowSpace->SpaceScope(), ioRowSpace); }
+  // the AddRowSpace() boolean return equals ev->Good().
+
+  mork_bool  CutRowSpace(morkEnv* ev, mork_scope inScope)
+  { return this->CutNode(ev, inScope); }
+  // The CutRowSpace() boolean return indicates whether removal happened. 
+  
+  morkRowSpace*  GetRowSpace(morkEnv* ev, mork_scope inScope)
+  { return (morkRowSpace*) this->GetNode(ev, inScope); }
+  // Note the returned space does NOT have an increase in refcount for this.
+
+  mork_num CutAllRowSpaces(morkEnv* ev)
+  { return this->CutAllNodes(ev); }
+  // CutAllRowSpaces() releases all the referenced table values.
+};
+
+class morkRowSpaceMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+  morkRowSpaceMapIter(morkEnv* ev, morkRowSpaceMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkRowSpaceMapIter( ) : morkMapIter()  { }
+  void InitRowSpaceMapIter(morkEnv* ev, morkRowSpaceMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change*
+  FirstRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace)
+  { return this->First(ev, outScope, outRowSpace); }
+  
+  mork_change*
+  NextRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace)
+  { return this->Next(ev, outScope, outRowSpace); }
+  
+  mork_change*
+  HereRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace)
+  { return this->Here(ev, outScope, outRowSpace); }
+  
+  mork_change*
+  CutHereRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace)
+  { return this->CutHere(ev, outScope, outRowSpace); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROWSPACE_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkSearchRowCursor.cpp
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKSEARCHROWCURSOR_
+#include "morkSearchRowCursor.h"
+#endif
+
+#ifndef _MORKUNIQROWCURSOR_
+#include "morkUniqRowCursor.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkSearchRowCursor::CloseMorkNode(morkEnv* ev) // CloseSearchRowCursor() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseSearchRowCursor(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkSearchRowCursor::~morkSearchRowCursor() // CloseSearchRowCursor() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkSearchRowCursor::morkSearchRowCursor(morkEnv* ev,
+  const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos)
+: morkTableRowCursor(ev, inUsage, ioHeap, ioTable, inRowPos)
+// , mSortingRowCursor_Sorting( 0 )
+{
+  if ( ev->Good() )
+  {
+    if ( ioTable )
+    {
+      // morkSorting::SlotWeakSorting(ioSorting, ev, &mSortingRowCursor_Sorting);
+      if ( ev->Good() )
+      {
+        // mNode_Derived = morkDerived_kTableRowCursor;
+        // mNode_Derived must stay equal to  kTableRowCursor
+      }
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+/*public non-poly*/ void
+morkSearchRowCursor::CloseSearchRowCursor(morkEnv* ev) 
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      // morkSorting::SlotWeakSorting((morkSorting*) 0, ev, &mSortingRowCursor_Sorting);
+      this->CloseTableRowCursor(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkSearchRowCursor::NonSearchRowCursorTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkSearchRowCursor");
+}
+
+morkUniqRowCursor*
+morkSearchRowCursor::MakeUniqCursor(morkEnv* ev)
+{
+  morkUniqRowCursor* outCursor = 0;
+  
+  return outCursor;
+}
+
+#if 0
+orkinTableRowCursor*
+morkSearchRowCursor::AcquireUniqueRowCursorHandle(morkEnv* ev)
+{
+  orkinTableRowCursor* outCursor = 0;
+  
+  morkUniqRowCursor* uniqCursor = this->MakeUniqCursor(ev);
+  if ( uniqCursor )
+  {
+    outCursor = uniqCursor->AcquireTableRowCursorHandle(ev);
+    uniqCursor->CutStrongRef(ev);
+  }
+  return outCursor;
+}
+#endif
+mork_bool
+morkSearchRowCursor::CanHaveDupRowMembers(morkEnv* ev)
+{
+  return morkBool_kTrue; // true is correct
+}
+
+mork_count
+morkSearchRowCursor::GetMemberCount(morkEnv* ev)
+{
+  morkTable* table = mTableRowCursor_Table;
+  if ( table )
+    return table->mTable_RowArray.mArray_Fill;
+  else
+    return 0;
+}
+
+morkRow*
+morkSearchRowCursor::NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos)
+{
+  morkRow* outRow = 0;
+  mork_pos pos = -1;
+  
+  morkTable* table = mTableRowCursor_Table;
+  if ( table )
+  {
+  }
+  else
+    ev->NilPointerError();
+
+  *outPos = pos;
+  return outRow;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkSearchRowCursor.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKSEARCHROWCURSOR_
+#define _MORKSEARCHROWCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKTABLEROWCURSOR_
+#include "morkTableRowCursor.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class morkUniqRowCursor;
+class orkinTableRowCursor;
+// #define morkDerived_kSearchRowCursor  /*i*/ 0x7352 /* ascii 'sR' */
+
+class morkSearchRowCursor : public morkTableRowCursor { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+  // morkFactory* mObject_Factory;  // weak ref to suite factory
+
+  // mork_seed  mCursor_Seed;
+  // mork_pos   mCursor_Pos;
+  // mork_bool  mCursor_DoFailOnSeedOutOfSync;
+  // mork_u1    mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+  // morkTable*  mTableRowCursor_Table; // weak ref to table
+
+public: // state is public because the entire Mork system is private
+    
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseSearchRowCursor()
+  virtual ~morkSearchRowCursor(); // assert that close executed earlier
+  
+public: // morkSearchRowCursor construction & destruction
+  morkSearchRowCursor(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos);
+  void CloseSearchRowCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkSearchRowCursor(const morkSearchRowCursor& other);
+  morkSearchRowCursor& operator=(const morkSearchRowCursor& other);
+
+public: // dynamic type identification
+  // mork_bool IsSearchRowCursor() const
+  // { return IsNode() && mNode_Derived == morkDerived_kSearchRowCursor; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonSearchRowCursorTypeError(morkEnv* ev);
+
+public: // uniquify
+
+  morkUniqRowCursor* MakeUniqCursor(morkEnv* ev);
+
+public: // other search row cursor methods
+
+  virtual mork_bool CanHaveDupRowMembers(morkEnv* ev);
+  virtual mork_count GetMemberCount(morkEnv* ev);
+
+#if 0
+  virtual orkinTableRowCursor* AcquireUniqueRowCursorHandle(morkEnv* ev);
+#endif
+
+  // virtual mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid);
+  virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakSearchRowCursor(morkSearchRowCursor* me,
+    morkEnv* ev, morkSearchRowCursor** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongSearchRowCursor(morkSearchRowCursor* me,
+    morkEnv* ev, morkSearchRowCursor** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSEARCHROWCURSOR_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkSink.cpp
@@ -0,0 +1,324 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKSINK_
+#include "morkSink.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*virtual*/ morkSink::~morkSink()
+{
+  mSink_At = 0;
+  mSink_End = 0;
+}
+
+/*virtual*/ void
+morkSpool::FlushSink(morkEnv* ev) // sync mSpool_Coil->mBuf_Fill
+{
+  morkCoil* coil = mSpool_Coil;
+  if ( coil )
+  {
+    mork_u1* body = (mork_u1*) coil->mBuf_Body;
+    if ( body )
+    {
+      mork_u1* at = mSink_At;
+      mork_u1* end = mSink_End;
+      if ( at >= body && at <= end ) // expected cursor order?
+      {
+        mork_fill fill = (mork_fill) (at - body); // current content size
+        if ( fill <= coil->mBlob_Size )
+          coil->mBuf_Fill = fill;
+        else
+        {
+          coil->BlobFillOverSizeError(ev);
+          coil->mBuf_Fill = coil->mBlob_Size; // make it safe
+        }
+      }
+      else
+        this->BadSpoolCursorOrderError(ev);
+    }
+    else
+      coil->NilBufBodyError(ev);
+  }
+  else
+    this->NilSpoolCoilError(ev);
+}
+
+/*virtual*/ void
+morkSpool::SpillPutc(morkEnv* ev, int c) // grow coil and write byte
+{
+  morkCoil* coil = mSpool_Coil;
+  if ( coil )
+  {
+    mork_u1* body = (mork_u1*) coil->mBuf_Body;
+    if ( body )
+    {
+      mork_u1* at = mSink_At;
+      mork_u1* end = mSink_End;
+      if ( at >= body && at <= end ) // expected cursor order?
+      {
+        mork_size size = coil->mBlob_Size;
+        mork_fill fill = (mork_fill) (at - body); // current content size
+        if ( fill <= size ) // less content than medium size?
+        {
+          coil->mBuf_Fill = fill;
+          if ( at >= end ) // need to grow the coil?
+          {
+            if ( size > 2048 ) // grow slower over 2K?
+              size += 512;
+            else
+            {
+              mork_size growth = ( size * 4 ) / 3; // grow by 33%
+              if ( growth < 64 ) // grow faster under (64 * 3)?
+                growth = 64;
+              size += growth;
+            }
+            if ( coil->GrowCoil(ev, size) ) // made coil bigger?
+            {
+              body = (mork_u1*) coil->mBuf_Body;
+              if ( body ) // have a coil body?
+              {
+                mSink_At = at = body + fill;
+                mSink_End = end = body + coil->mBlob_Size;
+              }
+              else
+                coil->NilBufBodyError(ev);
+            }
+          }
+          if ( ev->Good() ) // seem ready to write byte c?
+          {
+            if ( at < end ) // morkSink::Putc() would succeed?
+            {
+              *at++ = (mork_u1) c;
+              mSink_At = at;
+              coil->mBuf_Fill = fill + 1;
+            }
+            else
+              this->BadSpoolCursorOrderError(ev);
+          }
+        }
+        else // fill exceeds size
+        {
+          coil->BlobFillOverSizeError(ev);
+          coil->mBuf_Fill = coil->mBlob_Size; // make it safe
+        }
+      }
+      else
+        this->BadSpoolCursorOrderError(ev);
+    }
+    else
+      coil->NilBufBodyError(ev);
+  }
+  else
+    this->NilSpoolCoilError(ev);
+}
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+// public: // public non-poly morkSink methods
+
+/*virtual*/
+morkSpool::~morkSpool()
+// Zero all slots to show this sink is disabled, but destroy no memory.
+// Note it is typically unnecessary to flush this coil sink, since all
+// content is written directly to the coil without any buffering.
+{
+  mSink_At = 0;
+  mSink_End = 0;
+  mSpool_Coil = 0;
+}
+
+morkSpool::morkSpool(morkEnv* ev, morkCoil* ioCoil)
+// After installing the coil, calls Seek(ev, 0) to prepare for writing.
+: morkSink()
+, mSpool_Coil( 0 )
+{
+  mSink_At = 0; // set correctly later in Seek()
+  mSink_End = 0; // set correctly later in Seek()
+  
+  if ( ev->Good() )
+  {
+    if ( ioCoil )
+    {
+      mSpool_Coil = ioCoil;
+      this->Seek(ev, /*pos*/ 0);
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+// ----- All boolean return values below are equal to ev->Good(): -----
+
+/*static*/ void
+morkSpool::BadSpoolCursorOrderError(morkEnv* ev)
+{
+  ev->NewError("bad morkSpool cursor order");
+}
+
+/*static*/ void
+morkSpool::NilSpoolCoilError(morkEnv* ev)
+{
+  ev->NewError("nil mSpool_Coil");
+}
+
+mork_bool
+morkSpool::Seek(morkEnv* ev, mork_pos inPos)
+// Changed the current write position in coil's buffer to inPos.
+// For example, to start writing the coil from scratch, use inPos==0.
+{
+  morkCoil* coil = mSpool_Coil;
+  if ( coil )
+  {
+    mork_size minSize = (mork_size) (inPos + 64);
+    
+    if ( coil->mBlob_Size < minSize )
+      coil->GrowCoil(ev, minSize);
+      
+    if ( ev->Good() )
+    {
+      coil->mBuf_Fill = (mork_fill) inPos;
+      mork_u1* body = (mork_u1*) coil->mBuf_Body;
+      if ( body )
+      {
+        mSink_At = body + inPos;
+        mSink_End = body + coil->mBlob_Size;
+      }
+      else
+        coil->NilBufBodyError(ev);
+    }
+  }
+  else
+    this->NilSpoolCoilError(ev);
+    
+  return ev->Good();
+}
+
+mork_bool
+morkSpool::Write(morkEnv* ev, const void* inBuf, mork_size inSize)
+// write inSize bytes of inBuf to current position inside coil's buffer
+{
+  // This method is conceptually very similar to morkStream::Write(),
+  // and this code was written while looking at that method for clues.
+ 
+  morkCoil* coil = mSpool_Coil;
+  if ( coil )
+  {
+    mork_u1* body = (mork_u1*) coil->mBuf_Body;
+    if ( body )
+    {
+      if ( inBuf && inSize ) // anything to write?
+      {
+        mork_u1* at = mSink_At;
+        mork_u1* end = mSink_End;
+        if ( at >= body && at <= end ) // expected cursor order?
+        {
+          // note coil->mBuf_Fill can be stale after morkSink::Putc():
+          mork_pos fill = at - body; // current content size
+          mork_num space = (mork_num) (end - at); // space left in body
+          if ( space < inSize ) // not enough to hold write?
+          {
+            mork_size minGrowth = space + 16;
+            mork_size minSize = coil->mBlob_Size + minGrowth;
+            if ( coil->GrowCoil(ev, minSize) )
+            {
+              body = (mork_u1*) coil->mBuf_Body;
+              if ( body )
+              {
+                mSink_At = at = body + fill;
+                mSink_End = end = body + coil->mBlob_Size;
+                space = (mork_num) (end - at); // space left in body
+              }
+              else
+                coil->NilBufBodyError(ev);
+            }
+          }
+          if ( ev->Good() )
+          {
+            if ( space >= inSize ) // enough room to hold write?
+            {
+              MORK_MEMCPY(at, inBuf, inSize); // into body
+              mSink_At = at + inSize; // advance past written bytes
+              coil->mBuf_Fill = fill + inSize; // "flush" to fix fill
+            }
+            else
+              ev->NewError("insufficient morkSpool space");
+          }
+        }
+        else
+          this->BadSpoolCursorOrderError(ev);
+      }
+    }
+    else
+      coil->NilBufBodyError(ev);
+  }
+  else
+    this->NilSpoolCoilError(ev);
+  
+  return ev->Good();
+}
+
+mork_bool
+morkSpool::PutString(morkEnv* ev, const char* inString)
+// call Write() with inBuf=inString and inSize=strlen(inString),
+// unless inString is null, in which case we then do nothing at all.
+{
+  if ( inString )
+  {
+    mork_size size = MORK_STRLEN(inString);
+    this->Write(ev, inString, size);
+  }
+  return ev->Good();
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkSink.h
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKSINK_
+#define _MORKSINK_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| morkSink is intended to be a very cheap buffered i/o sink which
+**| writes to bufs and other strings a single byte at a time.  The
+**| basic idea is that writing a single byte has a very cheap average
+**| cost, because a polymophic function call need only occur when the
+**| space between At and End is exhausted.  The rest of the time a
+**| very cheap inline method will write a byte, and then bump a pointer.
+**|
+**|| At: the current position in some sequence of bytes at which to
+**| write the next byte put into the sink.  Presumably At points into
+**| the private storage of some space which is not yet filled (except
+**| when At reaches End, and the overflow must then spill).  Note both
+**| At and End are zeroed in the destructor to help show that a sink
+**| is no longer usable; this is safe because At==End causes the case
+**| where SpillPutc() is called to handled an exhausted buffer space.
+**|
+**|| End: an address one byte past the last byte which can be written
+**| without needing to make a buffer larger than previously.  When At
+**| and End are equal, this means there is no space to write a byte,
+**| and that some underlying buffer space must be grown before another
+**| byte can be written.  Note At must always be less than or equal to
+**| End, and otherwise an important invariant has failed severely.
+**|
+**|| Buf: this original class slot has been commented out in the new
+**| and more abstract version of this sink class, but the general idea
+**| behind this slot should be explained to help design subclasses.
+**| Each subclass should provide space into which At and End can point,
+**| where End is beyond the last writable byte, and At is less than or
+**| equal to this point inside the same buffer.  With some kinds of
+**| medium, such as writing to an instance of morkBlob, it is feasible
+**| to point directly into the final resting place for all the content
+**| written to the medium.  Other mediums such as files, which write
+**| only through function calls, will typically need a local buffer
+**| to efficiently accumulate many bytes between such function calls.
+**|
+**|| FlushSink: this flush method should move any buffered content to 
+**| its final destination.  For example, for buffered writes to a
+**| string medium, where string methods are function calls and not just
+**| inline macros, it is faster to accumulate many bytes in a small
+**| local buffer and then move these en masse later in a single call.
+**|
+**|| SpillPutc: when At is greater than or equal to End, this means an
+**| underlying buffer has become full, so the buffer must be flushed
+**| before a new byte can be written.  The intention is that SpillPutc()
+**| will be equivalent to calling FlushSink() followed by another call
+**| to Putc(), where the flush is expected to make At less then End once
+**| again.  Except that FlushSink() need not make the underlying buffer
+**| any larger, and SpillPutc() typically must make room for more bytes.
+**| Note subclasses might want to guard against the case that both At
+**| and End are null, which happens when a sink is destroyed, which sets
+**| both these pointers to null as an indication the sink is disabled.
+|*/
+class morkSink {
+    
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public sink virtual methods
+
+  virtual void FlushSink(morkEnv* ev) = 0;
+  virtual void SpillPutc(morkEnv* ev, int c) = 0;
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // member variables
+
+  mork_u1*     mSink_At;     // pointer into mSink_Buf
+  mork_u1*     mSink_End;    // one byte past last content byte
+
+// define morkSink_kBufSize 256 /* small enough to go on stack */
+
+  // mork_u1      mSink_Buf[ morkSink_kBufSize + 4 ];
+  // want plus one for any needed end null byte; use plus 4 for alignment
+   
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public non-poly morkSink methods
+
+  virtual ~morkSink(); // zero both At and End; virtual for subclasses
+  morkSink() { } // does nothing; subclasses must set At and End suitably
+
+  void Putc(morkEnv* ev, int c)
+  { 
+    if ( mSink_At < mSink_End )
+      *mSink_At++ = (mork_u1) c;
+    else
+      this->SpillPutc(ev, c);
+  }
+};
+
+/*| morkSpool: an output sink that efficiently writes individual bytes
+**| or entire byte sequences to a coil instance, which grows as needed by
+**| using the heap instance in the coil to grow the internal buffer.
+**|
+**|| Note we do not "own" the coil referenced by mSpool_Coil, and
+**| the lifetime of the coil is expected to equal or exceed that of this
+**| sink by some external means.  Typical usage might involve keeping an
+**| instance of morkCoil and an instance of morkSpool in the same
+**| owning parent object, which uses the spool with the associated coil.
+|*/
+class morkSpool : public morkSink { // for buffered i/o to a morkCoil
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public sink virtual methods
+
+  // when morkSink::Putc() moves mSink_At, mSpool_Coil->mBuf_Fill is wrong:
+
+  virtual void FlushSink(morkEnv* ev); // sync mSpool_Coil->mBuf_Fill
+  virtual void SpillPutc(morkEnv* ev, int c); // grow coil and write byte
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // member variables
+  morkCoil*   mSpool_Coil; // destination medium for written bytes
+    
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public non-poly morkSink methods
+
+  static void BadSpoolCursorOrderError(morkEnv* ev);
+  static void NilSpoolCoilError(morkEnv* ev);
+
+  virtual ~morkSpool();
+  // Zero all slots to show this sink is disabled, but destroy no memory.
+  // Note it is typically unnecessary to flush this coil sink, since all
+  // content is written directly to the coil without any buffering.
+  
+  morkSpool(morkEnv* ev, morkCoil* ioCoil);
+  // After installing the coil, calls Seek(ev, 0) to prepare for writing.
+  
+  // ----- All boolean return values below are equal to ev->Good(): -----
+
+  mork_bool Seek(morkEnv* ev, mork_pos inPos);
+  // Changed the current write position in coil's buffer to inPos.
+  // For example, to start writing the coil from scratch, use inPos==0.
+
+  mork_bool Write(morkEnv* ev, const void* inBuf, mork_size inSize);
+  // write inSize bytes of inBuf to current position inside coil's buffer
+
+  mork_bool PutBuf(morkEnv* ev, const morkBuf& inBuffer)
+  { return this->Write(ev, inBuffer.mBuf_Body, inBuffer.mBuf_Fill); }
+  
+  mork_bool PutString(morkEnv* ev, const char* inString);
+  // call Write() with inBuf=inString and inSize=strlen(inString),
+  // unless inString is null, in which case we then do nothing at all.
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSINK_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkSpace.cpp
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkSpace::CloseMorkNode(morkEnv* ev) // CloseSpace() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseSpace(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkSpace::~morkSpace() // assert CloseSpace() executed earlier
+{
+  MORK_ASSERT(SpaceScope()==0);
+  MORK_ASSERT(mSpace_Store==0);
+  MORK_ASSERT(this->IsShutNode());
+}    
+
+/*public non-poly*/
+//morkSpace::morkSpace(morkEnv* ev, const morkUsage& inUsage,
+//  nsIMdbHeap* ioNodeHeap, const morkMapForm& inForm,
+//  nsIMdbHeap* ioSlotHeap)
+//: morkNode(ev, inUsage, ioNodeHeap)
+//, mSpace_Map(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
+//{
+//  ev->StubMethodOnlyError();
+//}
+
+/*public non-poly*/
+morkSpace::morkSpace(morkEnv* ev,
+  const morkUsage& inUsage, mork_scope inScope, morkStore* ioStore,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkBead(ev, inUsage, ioHeap, inScope)
+, mSpace_Store( 0 )
+, mSpace_DoAutoIDs( morkBool_kFalse )
+, mSpace_HaveDoneAutoIDs( morkBool_kFalse )
+, mSpace_CanDirty( morkBool_kFalse ) // only when store can be dirtied
+{
+  if ( ev->Good() )
+  {
+    if ( ioStore && ioSlotHeap )
+    {
+      morkStore::SlotWeakStore(ioStore, ev, &mSpace_Store);
+
+      mSpace_CanDirty = ioStore->mStore_CanDirty;
+      if ( mSpace_CanDirty ) // this new space dirties the store?
+        this->MaybeDirtyStoreAndSpace();
+        
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kSpace;
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+/*public non-poly*/ void
+morkSpace::CloseSpace(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      morkStore::SlotWeakStore((morkStore*) 0, ev, &mSpace_Store);
+      mBead_Color = 0; // this->CloseBead();
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void 
+morkSpace::NonAsciiSpaceScopeName(morkEnv* ev)
+{
+  ev->NewError("SpaceScope() > 0x7F");
+}
+
+/*static*/ void 
+morkSpace::NilSpaceStoreError(morkEnv* ev)
+{
+  ev->NewError("nil mSpace_Store");
+}
+
+morkPool* morkSpace::GetSpaceStorePool() const
+{
+  return &mSpace_Store->mStore_Pool;
+}
+
+mork_bool morkSpace::MaybeDirtyStoreAndSpace()
+{
+  morkStore* store = mSpace_Store;
+  if ( store && store->mStore_CanDirty )
+  {
+    store->SetStoreDirty();
+    mSpace_CanDirty = morkBool_kTrue;
+  }
+  
+  if ( mSpace_CanDirty )
+  {
+    this->SetSpaceDirty();
+    return morkBool_kTrue;
+  }
+  
+  return morkBool_kFalse;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkSpace.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKSPACE_
+#define _MORKSPACE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKBEAD_
+#include "morkBead.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkSpace_kInitialSpaceSlots  /*i*/ 1024 /* default */
+#define morkDerived_kSpace  /*i*/ 0x5370 /* ascii 'Sp' */
+
+/*| morkSpace:
+|*/
+class morkSpace : public morkBead { // 
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+  // mork_color      mBead_Color;   // ID for this bead
+
+public: // bead color setter & getter replace obsolete member mTable_Id:
+
+  mork_tid     SpaceScope() const { return mBead_Color; }
+  void         SetSpaceScope(mork_scope inScope) { mBead_Color = inScope; }
+
+public: // state is public because the entire Mork system is private
+
+  morkStore*  mSpace_Store; // weak ref to containing store
+    
+  mork_bool   mSpace_DoAutoIDs;    // whether db should assign member IDs
+  mork_bool   mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs
+  mork_bool   mSpace_CanDirty; // changes imply the store becomes dirty?
+  mork_u1     mSpace_Pad;    // pad to u4 alignment
+
+public: // more specific dirty methods for space:
+  void SetSpaceDirty() { this->SetNodeDirty(); }
+  void SetSpaceClean() { this->SetNodeClean(); }
+  
+  mork_bool IsSpaceClean() const { return this->IsNodeClean(); }
+  mork_bool IsSpaceDirty() const { return this->IsNodeDirty(); }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseSpace() only if open
+  virtual ~morkSpace(); // assert that CloseSpace() executed earlier
+  
+public: // morkMap construction & destruction
+  //morkSpace(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap,
+  //  const morkMapForm& inForm, nsIMdbHeap* ioSlotHeap);
+  
+  morkSpace(morkEnv* ev, const morkUsage& inUsage,mork_scope inScope, 
+    morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap);
+  void CloseSpace(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsSpace() const
+  { return IsNode() && mNode_Derived == morkDerived_kSpace; }
+// } ===== end morkNode methods =====
+
+public: // other space methods
+  
+  mork_bool MaybeDirtyStoreAndSpace();
+
+  static void NonAsciiSpaceScopeName(morkEnv* ev);
+  static void NilSpaceStoreError(morkEnv* ev);
+
+  morkPool* GetSpaceStorePool() const;
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakSpace(morkSpace* me,
+    morkEnv* ev, morkSpace** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongSpace(morkSpace* me,
+    morkEnv* ev, morkSpace** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSPACE_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkStore.cpp
@@ -0,0 +1,2330 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+  
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKFACTORY_
+#include "morkFactory.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKTHUMB_
+#include "morkThumb.h"
+#endif
+// #ifndef _MORKFILE_
+// #include "morkFile.h"
+// #endif
+
+#ifndef _MORKBUILDER_
+#include "morkBuilder.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKPORTTABLECURSOR_
+#include "morkPortTableCursor.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+#include "nsCOMPtr.h"
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkStore::CloseMorkNode(morkEnv* ev) // ClosePort() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseStore(ev);
+    this->MarkShut();
+  }
+}
+
+/*public non-poly*/ void
+morkStore::ClosePort(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      morkFactory::SlotWeakFactory((morkFactory*) 0, ev, &mPort_Factory);
+      nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mPort_Heap);
+      this->CloseObject(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+/*public virtual*/
+morkStore::~morkStore() // assert CloseStore() executed earlier
+{
+  MOZ_COUNT_DTOR(morkStore);
+  if (IsOpenNode())
+    CloseMorkNode(mMorkEnv);
+  MORK_ASSERT(this->IsShutNode());
+  MORK_ASSERT(mStore_File==0);
+  MORK_ASSERT(mStore_InStream==0);
+  MORK_ASSERT(mStore_OutStream==0);
+  MORK_ASSERT(mStore_Builder==0);
+  MORK_ASSERT(mStore_OidAtomSpace==0);
+  MORK_ASSERT(mStore_GroundAtomSpace==0);
+  MORK_ASSERT(mStore_GroundColumnSpace==0);
+  MORK_ASSERT(mStore_RowSpaces.IsShutNode());
+  MORK_ASSERT(mStore_AtomSpaces.IsShutNode());
+  MORK_ASSERT(mStore_Pool.IsShutNode());
+}
+
+/*public non-poly*/
+morkStore::morkStore(morkEnv* ev, const morkUsage& inUsage,
+     nsIMdbHeap* ioNodeHeap, // the heap (if any) for this node instance
+     morkFactory* inFactory, // the factory for this
+     nsIMdbHeap* ioPortHeap  // the heap to hold all content in the port
+     )
+: morkObject(ev, inUsage, ioNodeHeap, morkColor_kNone, (morkHandle*) 0)
+, mPort_Env( ev )
+, mPort_Factory( 0 )
+, mPort_Heap( 0 )
+, mStore_OidAtomSpace( 0 )
+, mStore_GroundAtomSpace( 0 )
+, mStore_GroundColumnSpace( 0 )
+
+, mStore_File( 0 )
+, mStore_InStream( 0 )
+, mStore_Builder( 0 )
+, mStore_OutStream( 0 )
+
+, mStore_RowSpaces(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap)
+, mStore_AtomSpaces(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap)
+, mStore_Zone(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap)
+, mStore_Pool(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap)
+
+, mStore_CommitGroupIdentity( 0 )
+
+, mStore_FirstCommitGroupPos( 0 )
+, mStore_SecondCommitGroupPos( 0 )
+
+// disable auto-assignment of atom IDs until someone knows it is okay:
+, mStore_CanAutoAssignAtomIdentity( morkBool_kFalse )
+, mStore_CanDirty( morkBool_kFalse ) // not until the store is open
+, mStore_CanWriteIncremental( morkBool_kTrue ) // always with few exceptions
+{
+  MOZ_COUNT_CTOR(morkStore);
+  if ( ev->Good() )
+  {
+    if ( inFactory && ioPortHeap )
+    {
+      morkFactory::SlotWeakFactory(inFactory, ev, &mPort_Factory);
+      nsIMdbHeap_SlotStrongHeap(ioPortHeap, ev, &mPort_Heap);
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kPort;
+    }
+    else
+      ev->NilPointerError();
+  }
+  if ( ev->Good() )
+  {
+    mNode_Derived = morkDerived_kStore;
+    
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkStore, morkObject, nsIMdbStore)
+
+/*public non-poly*/ void
+morkStore::CloseStore(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+
+      nsIMdbFile* file = mStore_File;
+      file->AddRef();
+
+      morkFactory::SlotWeakFactory((morkFactory*) 0, ev, &mPort_Factory);
+      nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mPort_Heap);
+      morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+        &mStore_OidAtomSpace);
+      morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+        &mStore_GroundAtomSpace);
+      morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+        &mStore_GroundColumnSpace);
+      mStore_RowSpaces.CloseMorkNode(ev);
+      mStore_AtomSpaces.CloseMorkNode(ev);
+      morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mStore_Builder);
+      
+      nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev,
+        &mStore_File);
+      
+      file->Release();
+
+      morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_InStream);
+      morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_OutStream);
+
+      mStore_Pool.CloseMorkNode(ev);
+      mStore_Zone.CloseMorkNode(ev);
+      this->ClosePort(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+
+mork_bool morkStore::DoPreferLargeOverCompressCommit(morkEnv* ev)
+  // true when mStore_CanWriteIncremental && store has file large enough 
+{
+  nsIMdbFile* file = mStore_File;
+  if ( file && mStore_CanWriteIncremental )
+  {
+    mdb_pos fileEof = 0;
+    file->Eof(ev->AsMdbEnv(), &fileEof);
+    if ( ev->Good() && fileEof > 128 )
+      return morkBool_kTrue;
+  }
+  return morkBool_kFalse;
+}
+
+mork_percent morkStore::PercentOfStoreWasted(morkEnv* ev)
+{
+  mork_percent outPercent = 0;
+  nsIMdbFile* file = mStore_File;
+  
+  if ( file )
+  {
+    mork_pos firstPos = mStore_FirstCommitGroupPos;
+    mork_pos secondPos = mStore_SecondCommitGroupPos;
+    if ( firstPos || secondPos )
+    {
+      if ( firstPos < 512 && secondPos > firstPos )
+        firstPos = secondPos; // better approximation of first commit
+        
+      mork_pos fileLength = 0;
+      file->Eof(ev->AsMdbEnv(), &fileLength); // end of file
+      if ( ev->Good() && fileLength > firstPos )
+      {
+        mork_size groupContent = fileLength - firstPos;
+        outPercent = ( groupContent * 100 ) / fileLength;
+      }
+    }
+  }
+  else
+    this->NilStoreFileError(ev);
+    
+  return outPercent;
+}
+
+void
+morkStore::SetStoreAndAllSpacesCanDirty(morkEnv* ev, mork_bool inCanDirty)
+{
+  mStore_CanDirty = inCanDirty;
+  
+  mork_change* c = 0;
+  mork_scope* key = 0; // ignore keys in maps
+
+  if ( ev->Good() )
+  {
+    morkAtomSpaceMapIter asi(ev, &mStore_AtomSpaces);
+
+    morkAtomSpace* atomSpace = 0; // old val node in the map
+    
+    for ( c = asi.FirstAtomSpace(ev, key, &atomSpace); c && ev->Good();
+          c = asi.NextAtomSpace(ev, key, &atomSpace) )
+    {
+      if ( atomSpace )
+      {
+        if ( atomSpace->IsAtomSpace() )
+          atomSpace->mSpace_CanDirty = inCanDirty;
+        else
+          atomSpace->NonAtomSpaceTypeError(ev);
+      }
+      else
+        ev->NilPointerError();
+    }
+  }
+
+  if ( ev->Good() )
+  {
+    morkRowSpaceMapIter rsi(ev, &mStore_RowSpaces);
+    morkRowSpace* rowSpace = 0; // old val node in the map
+    
+    for ( c = rsi.FirstRowSpace(ev, key, &rowSpace); c && ev->Good();
+          c = rsi.NextRowSpace(ev, key, &rowSpace) )
+    {
+      if ( rowSpace )
+      {
+        if ( rowSpace->IsRowSpace() )
+          rowSpace->mSpace_CanDirty = inCanDirty;
+        else
+          rowSpace->NonRowSpaceTypeError(ev);
+      }
+    }
+  }
+}
+
+void
+morkStore::RenumberAllCollectableContent(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+  // do nothing currently
+}
+
+nsIMdbStore*
+morkStore::AcquireStoreHandle(morkEnv* ev)
+{
+  return this;
+}
+
+
+morkFarBookAtom*
+morkStore::StageAliasAsFarBookAtom(morkEnv* ev, const morkMid* inMid,
+   morkAtomSpace* ioSpace, mork_cscode inForm)
+{
+  if ( inMid && inMid->mMid_Buf )
+  {
+    const morkBuf* buf = inMid->mMid_Buf;
+    mork_size length = buf->mBuf_Fill;
+    if ( length <= morkBookAtom_kMaxBodySize )
+    {
+      mork_aid dummyAid = 1;
+      //mStore_BookAtom.InitMaxBookAtom(ev, *buf, 
+      //  inForm, ioSpace, dummyAid);
+       
+      mStore_FarBookAtom.InitFarBookAtom(ev, *buf, 
+        inForm, ioSpace, dummyAid);
+      return &mStore_FarBookAtom;
+    }
+  }
+  else
+    ev->NilPointerError();
+    
+  return (morkFarBookAtom*) 0;
+}
+
+morkFarBookAtom*
+morkStore::StageYarnAsFarBookAtom(morkEnv* ev, const mdbYarn* inYarn,
+   morkAtomSpace* ioSpace)
+{
+  if ( inYarn && inYarn->mYarn_Buf )
+  {
+    mork_size length = inYarn->mYarn_Fill;
+    if ( length <= morkBookAtom_kMaxBodySize )
+    {
+      morkBuf buf(inYarn->mYarn_Buf, length);
+      mork_aid dummyAid = 1;
+      //mStore_BookAtom.InitMaxBookAtom(ev, buf, 
+      //  inYarn->mYarn_Form, ioSpace, dummyAid);
+      mStore_FarBookAtom.InitFarBookAtom(ev, buf, 
+        inYarn->mYarn_Form, ioSpace, dummyAid);
+      return &mStore_FarBookAtom;
+    }
+  }
+  else
+    ev->NilPointerError();
+    
+  return (morkFarBookAtom*) 0;
+}
+
+morkFarBookAtom*
+morkStore::StageStringAsFarBookAtom(morkEnv* ev, const char* inString,
+   mork_cscode inForm, morkAtomSpace* ioSpace)
+{
+  if ( inString )
+  {
+    mork_size length = MORK_STRLEN(inString);
+    if ( length <= morkBookAtom_kMaxBodySize )
+    {
+      morkBuf buf(inString, length);
+      mork_aid dummyAid = 1;
+      //mStore_BookAtom.InitMaxBookAtom(ev, buf, inForm, ioSpace, dummyAid);
+      mStore_FarBookAtom.InitFarBookAtom(ev, buf, inForm, ioSpace, dummyAid);
+      return &mStore_FarBookAtom;
+    }
+  }
+  else
+    ev->NilPointerError();
+    
+  return (morkFarBookAtom*) 0;
+}
+
+morkAtomSpace* morkStore::LazyGetOidAtomSpace(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+  if ( !mStore_OidAtomSpace )
+  {
+  }
+  return mStore_OidAtomSpace;
+}
+
+morkAtomSpace* morkStore::LazyGetGroundAtomSpace(morkEnv* ev)
+{
+  if ( !mStore_GroundAtomSpace )
+  {
+    mork_scope atomScope = morkStore_kValueSpaceScope;
+    nsIMdbHeap* heap = mPort_Heap;
+    morkAtomSpace* space = new(*heap, ev) 
+      morkAtomSpace(ev, morkUsage::kHeap, atomScope, this, heap, heap);
+      
+    if ( space ) // successful space creation?
+    {
+      this->MaybeDirtyStore();
+    
+      mStore_GroundAtomSpace = space; // transfer strong ref to this slot
+      mStore_AtomSpaces.AddAtomSpace(ev, space);
+    }
+  }
+  return mStore_GroundAtomSpace;
+}
+
+morkAtomSpace* morkStore::LazyGetGroundColumnSpace(morkEnv* ev)
+{
+  if ( !mStore_GroundColumnSpace )
+  {
+    mork_scope atomScope = morkStore_kGroundColumnSpace;
+    nsIMdbHeap* heap = mPort_Heap;
+    morkAtomSpace* space = new(*heap, ev) 
+      morkAtomSpace(ev, morkUsage::kHeap, atomScope, this, heap, heap);
+      
+    if ( space ) // successful space creation?
+    {
+      this->MaybeDirtyStore();
+    
+      mStore_GroundColumnSpace = space; // transfer strong ref to this slot
+      mStore_AtomSpaces.AddAtomSpace(ev, space);
+    }
+  }
+  return mStore_GroundColumnSpace;
+}
+
+morkStream* morkStore::LazyGetInStream(morkEnv* ev)
+{
+  if ( !mStore_InStream )
+  {
+    nsIMdbFile* file = mStore_File;
+    if ( file )
+    {
+      morkStream* stream = new(*mPort_Heap, ev) 
+        morkStream(ev, morkUsage::kHeap, mPort_Heap, file,
+          morkStore_kStreamBufSize, /*frozen*/ morkBool_kTrue);
+      if ( stream )
+      {
+        this->MaybeDirtyStore();
+        mStore_InStream = stream; // transfer strong ref to this slot
+      }
+    }
+    else
+      this->NilStoreFileError(ev);
+  }
+  return mStore_InStream;
+}
+
+morkStream* morkStore::LazyGetOutStream(morkEnv* ev)
+{
+  if ( !mStore_OutStream )
+  {
+    nsIMdbFile* file = mStore_File;
+    if ( file )
+    {
+      morkStream* stream = new(*mPort_Heap, ev) 
+        morkStream(ev, morkUsage::kHeap, mPort_Heap, file,
+          morkStore_kStreamBufSize, /*frozen*/ morkBool_kFalse);
+      if ( stream )
+      {
+        this->MaybeDirtyStore();
+        mStore_InStream = stream; // transfer strong ref to this slot
+      }
+    }
+    else
+      this->NilStoreFileError(ev);
+  }
+  return mStore_OutStream;
+}
+
+void
+morkStore::ForgetBuilder(morkEnv* ev)
+{
+  if ( mStore_Builder )
+    morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mStore_Builder);
+  if ( mStore_InStream )
+    morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_InStream);
+}
+
+morkBuilder* morkStore::LazyGetBuilder(morkEnv* ev)
+{
+  if ( !mStore_Builder )
+  {
+    morkStream* stream = this->LazyGetInStream(ev);
+    if ( stream )
+    {
+      nsIMdbHeap* heap = mPort_Heap;
+      morkBuilder* builder = new(*heap, ev) 
+        morkBuilder(ev, morkUsage::kHeap, heap, stream,
+          morkBuilder_kDefaultBytesPerParseSegment, heap, this);
+      if ( builder )
+      {
+        mStore_Builder = builder; // transfer strong ref to this slot
+      }
+    }
+  }
+  return mStore_Builder;
+}
+
+morkRowSpace*
+morkStore::LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope)
+{
+  morkRowSpace* outSpace = mStore_RowSpaces.GetRowSpace(ev, inRowScope);
+  if ( !outSpace && ev->Good() ) // try to make new space?
+  {
+    nsIMdbHeap* heap = mPort_Heap;
+    outSpace = new(*heap, ev) 
+      morkRowSpace(ev, morkUsage::kHeap, inRowScope, this, heap, heap);
+      
+    if ( outSpace ) // successful space creation?
+    {
+      this->MaybeDirtyStore();
+    
+      // note adding to node map creates its own strong ref...
+      if ( mStore_RowSpaces.AddRowSpace(ev, outSpace) )
+        outSpace->CutStrongRef(ev); // ...so we can drop our strong ref
+    }
+  }
+  return outSpace;
+}
+
+morkAtomSpace*
+morkStore::LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope)
+{
+  morkAtomSpace* outSpace = mStore_AtomSpaces.GetAtomSpace(ev, inAtomScope);
+  if ( !outSpace && ev->Good() ) // try to make new space?
+  {
+    if ( inAtomScope == morkStore_kValueSpaceScope )
+      outSpace = this->LazyGetGroundAtomSpace(ev);
+      
+    else if ( inAtomScope == morkStore_kGroundColumnSpace )
+      outSpace = this->LazyGetGroundColumnSpace(ev);
+    else
+    {
+      nsIMdbHeap* heap = mPort_Heap;
+      outSpace = new(*heap, ev) 
+        morkAtomSpace(ev, morkUsage::kHeap, inAtomScope, this, heap, heap);
+        
+      if ( outSpace ) // successful space creation?
+      {
+        this->MaybeDirtyStore();
+    
+        // note adding to node map creates its own strong ref...
+        if ( mStore_AtomSpaces.AddAtomSpace(ev, outSpace) )
+          outSpace->CutStrongRef(ev); // ...so we can drop our strong ref
+      }
+    }
+  }
+  return outSpace;
+}
+
+/*static*/ void
+morkStore::NonStoreTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkStore");
+}
+
+/*static*/ void
+morkStore::NilStoreFileError(morkEnv* ev)
+{
+  ev->NewError("nil mStore_File");
+}
+
+/*static*/ void
+morkStore::CannotAutoAssignAtomIdentityError(morkEnv* ev)
+{
+  ev->NewError("false mStore_CanAutoAssignAtomIdentity");
+}
+
+
+mork_bool
+morkStore::OpenStoreFile(morkEnv* ev, mork_bool inFrozen,
+    // const char* inFilePath,
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy)
+{
+  MORK_USED_2(inOpenPolicy,inFrozen);
+  nsIMdbFile_SlotStrongFile(ioFile, ev, &mStore_File);
+  
+  // if ( ev->Good() )
+  // {
+  //   morkFile* file = morkFile::OpenOldFile(ev, mPort_Heap,
+  //     inFilePath, inFrozen);
+  //   if ( ioFile )
+  //   {
+  //     if ( ev->Good() )
+  //       morkFile::SlotStrongFile(file, ev, &mStore_File);
+  //     else
+  //       file->CutStrongRef(ev);
+  //       
+  //   }
+  // }
+  return ev->Good();
+}
+
+mork_bool
+morkStore::CreateStoreFile(morkEnv* ev,
+    // const char* inFilePath,
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy)
+{
+  MORK_USED_1(inOpenPolicy);
+  nsIMdbFile_SlotStrongFile(ioFile, ev, &mStore_File);
+  
+  return ev->Good();
+}
+
+morkAtom*
+morkStore::CopyAtom(morkEnv* ev, const morkAtom* inAtom)
+// copy inAtom (from some other store) over to this store
+{
+  morkAtom* outAtom = 0;
+  if ( inAtom )
+  {
+    mdbYarn yarn;
+    if ( inAtom->AliasYarn(&yarn) )
+      outAtom = this->YarnToAtom(ev, &yarn, PR_TRUE /* create */);
+  }
+  return outAtom;
+}
+ 
+morkAtom*
+morkStore::YarnToAtom(morkEnv* ev, const mdbYarn* inYarn, PRBool createIfMissing /* = PR_TRUE */)
+{
+  morkAtom* outAtom = 0;
+  if ( ev->Good() )
+  {
+    morkAtomSpace* groundSpace = this->LazyGetGroundAtomSpace(ev);
+    if ( groundSpace )
+    {
+      morkFarBookAtom* keyAtom =
+        this->StageYarnAsFarBookAtom(ev, inYarn, groundSpace);
+        
+      if ( keyAtom )
+      {
+        morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies;
+        outAtom = map->GetAtom(ev, keyAtom);
+        if ( !outAtom && createIfMissing)
+        {
+          this->MaybeDirtyStore();
+          outAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom);
+        }
+      }
+      else if ( ev->Good() )
+      {
+        morkBuf b(inYarn->mYarn_Buf, inYarn->mYarn_Fill);
+        morkZone* z = &mStore_Zone;
+        outAtom = mStore_Pool.NewAnonAtom(ev, b, inYarn->mYarn_Form, z);
+      }
+    }
+  }
+  return outAtom;
+}
+
+mork_bool
+morkStore::MidToOid(morkEnv* ev, const morkMid& inMid, mdbOid* outOid)
+{
+  *outOid = inMid.mMid_Oid;
+  const morkBuf* buf = inMid.mMid_Buf;
+  if ( buf && !outOid->mOid_Scope )
+  {
+    if ( buf->mBuf_Fill <= morkBookAtom_kMaxBodySize )
+    {
+      if ( buf->mBuf_Fill == 1 )
+      {
+        mork_u1* name = (mork_u1*) buf->mBuf_Body;
+        if ( name )
+        {
+          outOid->mOid_Scope = (mork_scope) *name;
+          return ev->Good();
+        }
+      }
+      morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev);
+      if ( groundSpace )
+      {
+        mork_cscode form = 0; // default
+        mork_aid aid = 1; // dummy
+        mStore_FarBookAtom.InitFarBookAtom(ev, *buf, form, groundSpace, aid);
+        morkFarBookAtom* keyAtom = &mStore_FarBookAtom;
+        morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies;
+        morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom);
+        if ( bookAtom )
+          outOid->mOid_Scope = bookAtom->mBookAtom_Id;
+        else
+        {
+          this->MaybeDirtyStore();
+          bookAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom);
+          if ( bookAtom )
+          {
+            outOid->mOid_Scope = bookAtom->mBookAtom_Id;
+            bookAtom->MakeCellUseForever(ev);
+          }
+        }
+      }
+    }
+  }
+  return ev->Good();
+}
+
+morkRow*
+morkStore::MidToRow(morkEnv* ev, const morkMid& inMid)
+{
+  mdbOid tempOid;
+  this->MidToOid(ev, inMid, &tempOid);
+  return this->OidToRow(ev, &tempOid);
+}
+
+morkTable*
+morkStore::MidToTable(morkEnv* ev, const morkMid& inMid)
+{
+  mdbOid tempOid;
+  this->MidToOid(ev, inMid, &tempOid);
+  return this->OidToTable(ev, &tempOid, /*metarow*/ (mdbOid*) 0);
+}
+
+mork_bool
+morkStore::MidToYarn(morkEnv* ev, const morkMid& inMid, mdbYarn* outYarn)
+{
+  mdbOid tempOid;
+  this->MidToOid(ev, inMid, &tempOid);
+  return this->OidToYarn(ev, tempOid, outYarn);
+}
+
+mork_bool
+morkStore::OidToYarn(morkEnv* ev, const mdbOid& inOid, mdbYarn* outYarn)
+{
+  morkBookAtom* atom = 0;
+      
+  morkAtomSpace* atomSpace = mStore_AtomSpaces.GetAtomSpace(ev, inOid.mOid_Scope);
+  if ( atomSpace )
+  {
+    morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids;
+    atom = map->GetAid(ev, (mork_aid) inOid.mOid_Id);
+  }
+  atom->GetYarn(outYarn); // note this is safe even when atom==nil
+
+  return ev->Good();
+}
+
+morkBookAtom*
+morkStore::MidToAtom(morkEnv* ev, const morkMid& inMid)
+{
+  morkBookAtom* outAtom = 0;
+  mdbOid oid;
+  if ( this->MidToOid(ev, inMid, &oid) )
+  {
+    morkAtomSpace* atomSpace = mStore_AtomSpaces.GetAtomSpace(ev, oid.mOid_Scope);
+    if ( atomSpace )
+    {
+      morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids;
+      outAtom = map->GetAid(ev, (mork_aid) oid.mOid_Id);
+    }
+  }
+  return outAtom;
+}
+
+/*static*/ void
+morkStore::SmallTokenToOneByteYarn(morkEnv* ev, mdb_token inToken,
+  mdbYarn* outYarn)
+{
+  MORK_USED_1(ev);
+  if ( outYarn->mYarn_Buf && outYarn->mYarn_Size ) // any space in yarn at all?
+  {
+    mork_u1* buf = (mork_u1*) outYarn->mYarn_Buf; // for byte arithmetic
+    buf[ 0 ] = (mork_u1) inToken; // write the single byte
+    outYarn->mYarn_Fill = 1;
+    outYarn->mYarn_More = 0;
+  }
+  else // just record we could not write the single byte
+  {
+    outYarn->mYarn_More = 1;
+    outYarn->mYarn_Fill = 0;
+  }
+}
+
+void
+morkStore::TokenToString(morkEnv* ev, mdb_token inToken, mdbYarn* outTokenName)
+{
+  if ( inToken > morkAtomSpace_kMaxSevenBitAid )
+  {
+    morkBookAtom* atom = 0;
+    morkAtomSpace* space = mStore_GroundColumnSpace;
+    if ( space )
+      atom = space->mAtomSpace_AtomAids.GetAid(ev, (mork_aid) inToken);
+      
+    atom->GetYarn(outTokenName); // note this is safe even when atom==nil
+  }
+  else // token is an "immediate" single byte string representation?
+    this->SmallTokenToOneByteYarn(ev, inToken, outTokenName);
+}
+  
+// void
+// morkStore::SyncTokenIdChange(morkEnv* ev, const morkBookAtom* inAtom,
+//   const mdbOid* inOid)
+// {
+// mork_token   mStore_MorkNoneToken;    // token for "mork:none"   // fill=9
+// mork_column  mStore_CharsetToken;     // token for "charset"     // fill=7
+// mork_column  mStore_AtomScopeToken;   // token for "atomScope"   // fill=9
+// mork_column  mStore_RowScopeToken;    // token for "rowScope"    // fill=8
+// mork_column  mStore_TableScopeToken;  // token for "tableScope"  // fill=10
+// mork_column  mStore_ColumnScopeToken; // token for "columnScope" // fill=11
+// mork_kind    mStore_TableKindToken;   // token for "tableKind"   // fill=9
+// ---------------------ruler-for-token-length-above---123456789012
+// 
+//   if ( inOid->mOid_Scope == morkStore_kColumnSpaceScope && inAtom->IsWeeBook() )
+//   {
+//     const mork_u1* body = ((const morkWeeBookAtom*) inAtom)->mWeeBookAtom_Body;
+//     mork_size size = inAtom->mAtom_Size;
+// 
+//     if ( size >= 7 && size <= 11 )
+//     {
+//       if ( size == 9 )
+//       {
+//         if ( *body == 'm' )
+//         {
+//           if ( MORK_MEMCMP(body, "mork:none", 9) == 0 )
+//             mStore_MorkNoneToken = inAtom->mBookAtom_Id;
+//         }
+//         else if ( *body == 'a' )
+//         {
+//           if ( MORK_MEMCMP(body, "atomScope", 9) == 0 )
+//             mStore_AtomScopeToken = inAtom->mBookAtom_Id;
+//         }
+//         else if ( *body == 't' )
+//         {
+//           if ( MORK_MEMCMP(body, "tableKind", 9) == 0 )
+//             mStore_TableKindToken = inAtom->mBookAtom_Id;
+//         }
+//       }
+//       else if ( size == 7 && *body == 'c' )
+//       {
+//         if ( MORK_MEMCMP(body, "charset", 7) == 0 )
+//           mStore_CharsetToken = inAtom->mBookAtom_Id;
+//       }
+//       else if ( size == 8 && *body == 'r' )
+//       {
+//         if ( MORK_MEMCMP(body, "rowScope", 8) == 0 )
+//           mStore_RowScopeToken = inAtom->mBookAtom_Id;
+//       }
+//       else if ( size == 10 && *body == 't' )
+//       {
+//         if ( MORK_MEMCMP(body, "tableScope", 10) == 0 )
+//           mStore_TableScopeToken = inAtom->mBookAtom_Id;
+//       }
+//       else if ( size == 11 && *body == 'c' )
+//       {
+//         if ( MORK_MEMCMP(body, "columnScope", 11) == 0 )
+//           mStore_ColumnScopeToken = inAtom->mBookAtom_Id;
+//       }
+//     }
+//   }
+// }
+
+morkAtom*
+morkStore::AddAlias(morkEnv* ev, const morkMid& inMid, mork_cscode inForm)
+{
+  morkBookAtom* outAtom = 0;
+  if ( ev->Good() )
+  {
+    const mdbOid* oid = &inMid.mMid_Oid;
+    morkAtomSpace* atomSpace = this->LazyGetAtomSpace(ev, oid->mOid_Scope);
+    if ( atomSpace )
+    {
+      morkFarBookAtom* keyAtom =
+        this->StageAliasAsFarBookAtom(ev, &inMid, atomSpace, inForm);
+      if ( keyAtom )
+      {
+         morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids;
+        outAtom = map->GetAid(ev, (mork_aid) oid->mOid_Id);
+        if ( outAtom )
+        {
+          if ( !outAtom->EqualFormAndBody(ev, keyAtom) )
+              ev->NewError("duplicate alias ID");
+        }
+        else
+        {
+          this->MaybeDirtyStore();
+          keyAtom->mBookAtom_Id = oid->mOid_Id;
+          outAtom = atomSpace->MakeBookAtomCopyWithAid(ev,
+            *keyAtom, (mork_aid) oid->mOid_Id);
+            
+          // if ( outAtom && outAtom->IsWeeBook() )
+          // {
+          //   if ( oid->mOid_Scope == morkStore_kColumnSpaceScope )
+          //   {
+          //    mork_size size = outAtom->mAtom_Size;
+          //     if ( size >= 7 && size <= 11 )
+          //       this->SyncTokenIdChange(ev, outAtom, oid);
+          //   }
+          // }
+        }
+      }
+    }
+  }
+  return outAtom;
+}
+
+#define morkStore_kMaxCopyTokenSize 512 /* if larger, cannot be copied */
+  
+mork_token
+morkStore::CopyToken(morkEnv* ev, mdb_token inToken, morkStore* inStore)
+// copy inToken from inStore over to this store
+{
+  mork_token outToken = 0;
+  if ( inStore == this ) // same store?
+    outToken = inToken; // just return token unchanged
+  else
+  {
+    char yarnBuf[ morkStore_kMaxCopyTokenSize ];
+    mdbYarn yarn;
+    yarn.mYarn_Buf = yarnBuf;
+    yarn.mYarn_Fill = 0;
+    yarn.mYarn_Size = morkStore_kMaxCopyTokenSize;
+    yarn.mYarn_More = 0;
+    yarn.mYarn_Form = 0;
+    yarn.mYarn_Grow = 0;
+    
+    inStore->TokenToString(ev, inToken, &yarn);
+    if ( ev->Good() )
+    {
+      morkBuf buf(yarn.mYarn_Buf, yarn.mYarn_Fill);
+      outToken = this->BufToToken(ev, &buf);
+    }
+  }
+  return outToken;
+}
+
+mork_token
+morkStore::BufToToken(morkEnv* ev, const morkBuf* inBuf)
+{
+  mork_token outToken = 0;
+  if ( ev->Good() )
+  {
+    const mork_u1* s = (const mork_u1*) inBuf->mBuf_Body;
+    mork_bool nonAscii = ( *s > 0x7F );
+    mork_size length = inBuf->mBuf_Fill;
+    if ( nonAscii || length > 1 ) // more than one byte?
+    {
+      mork_cscode form = 0; // default charset
+      morkAtomSpace* space = this->LazyGetGroundColumnSpace(ev);
+      if ( space )
+      {
+        morkFarBookAtom* keyAtom = 0;
+        if ( length <= morkBookAtom_kMaxBodySize )
+        {
+          mork_aid aid = 1; // dummy
+          //mStore_BookAtom.InitMaxBookAtom(ev, *inBuf, form, space, aid);
+          mStore_FarBookAtom.InitFarBookAtom(ev, *inBuf, form, space, aid);
+          keyAtom = &mStore_FarBookAtom;
+        }
+        if ( keyAtom )
+        {
+          morkAtomBodyMap* map = &space->mAtomSpace_AtomBodies;
+          morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom);
+          if ( bookAtom )
+            outToken = bookAtom->mBookAtom_Id;
+          else
+          {
+            this->MaybeDirtyStore();
+            bookAtom = space->MakeBookAtomCopy(ev, *keyAtom);
+            if ( bookAtom )
+            {
+              outToken = bookAtom->mBookAtom_Id;
+              bookAtom->MakeCellUseForever(ev);
+            }
+          }
+        }
+      }
+    }
+    else // only a single byte in inTokenName string:
+      outToken = *s;
+  }
+  
+  return outToken;
+}
+
+mork_token
+morkStore::StringToToken(morkEnv* ev, const char* inTokenName)
+{
+  mork_token outToken = 0;
+  if ( ev->Good() )
+  {
+    const mork_u1* s = (const mork_u1*) inTokenName;
+    mork_bool nonAscii = ( *s > 0x7F );
+    if ( nonAscii || ( *s && s[ 1 ] ) ) // more than one byte?
+    {
+      mork_cscode form = 0; // default charset
+      morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev);
+      if ( groundSpace )
+      {
+        morkFarBookAtom* keyAtom =
+          this->StageStringAsFarBookAtom(ev, inTokenName, form, groundSpace);
+        if ( keyAtom )
+        {
+          morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies;
+          morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom);
+          if ( bookAtom )
+            outToken = bookAtom->mBookAtom_Id;
+          else
+          {
+            this->MaybeDirtyStore();
+            bookAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom);
+            if ( bookAtom )
+            {
+              outToken = bookAtom->mBookAtom_Id;
+              bookAtom->MakeCellUseForever(ev);
+            }
+          }
+        }
+      }
+    }
+    else // only a single byte in inTokenName string:
+      outToken = *s;
+  }
+  
+  return outToken;
+}
+
+mork_token
+morkStore::QueryToken(morkEnv* ev, const char* inTokenName)
+{
+  mork_token outToken = 0;
+  if ( ev->Good() )
+  {
+    const mork_u1* s = (const mork_u1*) inTokenName;
+    mork_bool nonAscii = ( *s > 0x7F );
+    if ( nonAscii || ( *s && s[ 1 ] ) ) // more than one byte?
+    {
+      mork_cscode form = 0; // default charset
+      morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev);
+      if ( groundSpace )
+      {
+        morkFarBookAtom* keyAtom =
+          this->StageStringAsFarBookAtom(ev, inTokenName, form, groundSpace);
+        if ( keyAtom )
+        {
+          morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies;
+          morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom);
+          if ( bookAtom )
+          {
+            outToken = bookAtom->mBookAtom_Id;
+            bookAtom->MakeCellUseForever(ev);
+          }
+        }
+      }
+    }
+    else // only a single byte in inTokenName string:
+      outToken = *s;
+  }
+  
+  return outToken;
+}
+
+mork_bool
+morkStore::HasTableKind(morkEnv* ev, mdb_scope inRowScope, 
+  mdb_kind inTableKind, mdb_count* outTableCount)
+{
+  MORK_USED_2(inRowScope,inTableKind);
+  mork_bool outBool = morkBool_kFalse;
+  mdb_count tableCount = 0;
+
+  ev->StubMethodOnlyError();
+  
+  if ( outTableCount )
+    *outTableCount = tableCount;
+  return outBool;
+}
+
+morkTable*
+morkStore::GetTableKind(morkEnv* ev, mdb_scope inRowScope, 
+  mdb_kind inTableKind, mdb_count* outTableCount,
+  mdb_bool* outMustBeUnique)
+{
+  morkTable* outTable = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope);
+    if ( rowSpace )
+    {
+      outTable = rowSpace->FindTableByKind(ev, inTableKind);
+      if ( outTable )
+      {
+        if ( outTableCount )
+          *outTableCount = outTable->GetRowCount();
+        if ( outMustBeUnique )
+          *outMustBeUnique = outTable->IsTableUnique();
+      }
+    }
+  }
+  return outTable;
+}
+
+morkRow*
+morkStore::FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn,
+  const mdbYarn* inYarn)
+{
+  morkRow* outRow = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inScope);
+    if ( rowSpace )
+    {
+      outRow = rowSpace->FindRow(ev, inColumn, inYarn);
+    }
+  }
+  return outRow;
+}
+
+morkRow*
+morkStore::GetRow(morkEnv* ev, const mdbOid* inOid)
+{
+  morkRow* outRow = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+    if ( rowSpace )
+    {
+      outRow = rowSpace->mRowSpace_Rows.GetOid(ev, inOid);
+    }
+  }
+  return outRow;
+}
+
+morkTable*
+morkStore::GetTable(morkEnv* ev, const mdbOid* inOid)
+{
+  morkTable* outTable = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+    if ( rowSpace )
+    {
+      outTable = rowSpace->FindTableByTid(ev, inOid->mOid_Id);
+    }
+  }
+  return outTable;
+}
+  
+morkTable*
+morkStore::NewTable(morkEnv* ev, mdb_scope inRowScope,
+  mdb_kind inTableKind, mdb_bool inMustBeUnique,
+  const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying 
+{
+  morkTable* outTable = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope);
+    if ( rowSpace )
+      outTable = rowSpace->NewTable(ev, inTableKind, inMustBeUnique,
+        inOptionalMetaRowOid);
+  }
+  return outTable;
+}
+
+morkPortTableCursor*
+morkStore::GetPortTableCursor(morkEnv* ev, mdb_scope inRowScope,
+  mdb_kind inTableKind)
+{
+  morkPortTableCursor* outCursor = 0;
+  if ( ev->Good() )
+  {
+    nsIMdbHeap* heap = mPort_Heap;
+    outCursor = new(*heap, ev) 
+      morkPortTableCursor(ev, morkUsage::kHeap, heap, this,
+        inRowScope, inTableKind, heap);
+  }
+  NS_IF_ADDREF(outCursor);
+  return outCursor;
+}
+
+morkRow*
+morkStore::NewRow(morkEnv* ev, mdb_scope inRowScope)
+{
+  morkRow* outRow = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope);
+    if ( rowSpace )
+      outRow = rowSpace->NewRow(ev);
+  }
+  return outRow;
+}
+
+morkRow*
+morkStore::NewRowWithOid(morkEnv* ev, const mdbOid* inOid)
+{
+  morkRow* outRow = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+    if ( rowSpace )
+      outRow = rowSpace->NewRowWithOid(ev, inOid);
+  }
+  return outRow;
+}
+
+morkRow*
+morkStore::OidToRow(morkEnv* ev, const mdbOid* inOid)
+  // OidToRow() finds old row with oid, or makes new one if not found.
+{
+  morkRow* outRow = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+    if ( rowSpace )
+    {
+      outRow = rowSpace->mRowSpace_Rows.GetOid(ev, inOid);
+      if ( !outRow && ev->Good() )
+        outRow = rowSpace->NewRowWithOid(ev, inOid);
+    }
+  }
+  return outRow;
+}
+
+morkTable*
+morkStore::OidToTable(morkEnv* ev, const mdbOid* inOid,
+  const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying 
+  // OidToTable() finds old table with oid, or makes new one if not found.
+{
+  morkTable* outTable = 0;
+  if ( ev->Good() )
+  {
+    morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+    if ( rowSpace )
+    {
+      outTable = rowSpace->mRowSpace_Tables.GetTable(ev, inOid->mOid_Id);
+      if ( !outTable && ev->Good() )
+      {
+        mork_kind tableKind = morkStore_kNoneToken;
+        outTable = rowSpace->NewTableWithTid(ev, inOid->mOid_Id, tableKind,
+          inOptionalMetaRowOid);
+      }
+    }
+  }
+  return outTable;
+}
+
+// { ===== begin nsIMdbObject methods =====
+
+// { ----- begin ref counting for well-behaved cyclic graphs -----
+NS_IMETHODIMP
+morkStore::GetWeakRefCount(nsIMdbEnv* mev, // weak refs
+  mdb_count* outCount)
+{
+  *outCount = WeakRefsOnly();
+  return NS_OK;
+}  
+NS_IMETHODIMP
+morkStore::GetStrongRefCount(nsIMdbEnv* mev, // strong refs
+  mdb_count* outCount)
+{
+  *outCount = StrongRefsOnly();
+  return NS_OK;
+}
+// ### TODO - clean up this cast, if required
+NS_IMETHODIMP
+morkStore::AddWeakRef(nsIMdbEnv* mev)
+{
+  morkEnv *ev  = morkEnv::FromMdbEnv(mev);
+  return morkNode::AddWeakRef(ev);
+}
+NS_IMETHODIMP
+morkStore::AddStrongRef(nsIMdbEnv* mev)
+{
+  return AddRef();
+}
+
+NS_IMETHODIMP
+morkStore::CutWeakRef(nsIMdbEnv* mev)
+{
+  morkEnv *ev  = morkEnv::FromMdbEnv(mev);
+  return morkNode::CutWeakRef(ev);
+}
+NS_IMETHODIMP
+morkStore::CutStrongRef(nsIMdbEnv* mev)
+{
+  return Release();
+}
+
+NS_IMETHODIMP
+morkStore::CloseMdbObject(nsIMdbEnv* mev)
+{
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  CloseMorkNode(ev);
+  Release();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkStore::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
+{
+  *outOpen = IsOpenNode();
+  return NS_OK;
+}
+// } ----- end ref counting -----
+
+// } ===== end nsIMdbObject methods =====
+
+// { ===== begin nsIMdbPort methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkStore::GetIsPortReadonly(nsIMdbEnv* mev, mdb_bool* outBool)
+{
+  mdb_err outErr = 0;
+  mdb_bool isReadOnly = morkBool_kFalse;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    ev->StubMethodOnlyError();
+    outErr = ev->AsErr();
+  }
+  if ( outBool )
+    *outBool = isReadOnly;
+  return outErr;
+}
+
+morkEnv*
+morkStore::CanUseStore(nsIMdbEnv* mev,
+  mork_bool inMutable, mdb_err* outErr) const
+{
+  morkEnv* outEnv = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if (IsStore())
+      outEnv = ev;
+    else
+      NonStoreTypeError(ev);
+    *outErr = ev->AsErr();
+  }
+  MORK_ASSERT(outEnv);
+  return outEnv;
+}
+
+NS_IMETHODIMP
+morkStore::GetIsStore(nsIMdbEnv* mev, mdb_bool* outBool)
+{
+  MORK_USED_1(mev);
+ if ( outBool )
+    *outBool = morkBool_kTrue;
+  return 0;
+}
+
+NS_IMETHODIMP
+morkStore::GetIsStoreAndDirty(nsIMdbEnv* mev, mdb_bool* outBool)
+{
+  mdb_err outErr = 0;
+  mdb_bool isStoreAndDirty = morkBool_kFalse;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    ev->StubMethodOnlyError();
+    outErr = ev->AsErr();
+  }
+  if ( outBool )
+    *outBool = isStoreAndDirty;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetUsagePolicy(nsIMdbEnv* mev, 
+  mdbUsagePolicy* ioUsagePolicy)
+{
+  MORK_USED_1(ioUsagePolicy);
+  mdb_err outErr = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    ev->StubMethodOnlyError();
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::SetUsagePolicy(nsIMdbEnv* mev, 
+  const mdbUsagePolicy* inUsagePolicy)
+{
+  MORK_USED_1(inUsagePolicy);
+  mdb_err outErr = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    // ev->StubMethodOnlyError(); // okay to do nothing?
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin memory policy methods -----  
+NS_IMETHODIMP
+morkStore::IdleMemoryPurge( // do memory management already scheduled
+  nsIMdbEnv* mev, // context
+  mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed
+{
+  mdb_err outErr = 0;
+  mdb_size estimatedBytesFreed = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    // ev->StubMethodOnlyError(); // okay to do nothing?
+    outErr = ev->AsErr();
+  }
+  if ( outEstimatedBytesFreed )
+    *outEstimatedBytesFreed = estimatedBytesFreed;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::SessionMemoryPurge( // request specific footprint decrease
+  nsIMdbEnv* mev, // context
+  mdb_size inDesiredBytesFreed, // approximate number of bytes wanted
+  mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed
+{
+  MORK_USED_1(inDesiredBytesFreed);
+  mdb_err outErr = 0;
+  mdb_size estimate = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    // ev->StubMethodOnlyError(); // okay to do nothing?
+    outErr = ev->AsErr();
+  }
+  if ( outEstimatedBytesFreed )
+    *outEstimatedBytesFreed = estimate;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::PanicMemoryPurge( // desperately free all possible memory
+  nsIMdbEnv* mev, // context
+  mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed
+{
+  mdb_err outErr = 0;
+  mdb_size estimate = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    // ev->StubMethodOnlyError(); // okay to do nothing?
+    outErr = ev->AsErr();
+  }
+  if ( outEstimatedBytesFreed )
+    *outEstimatedBytesFreed = estimate;
+  return outErr;
+}
+// } ----- end memory policy methods -----
+
+// { ----- begin filepath methods -----
+NS_IMETHODIMP
+morkStore::GetPortFilePath(
+  nsIMdbEnv* mev, // context
+  mdbYarn* outFilePath, // name of file holding port content
+  mdbYarn* outFormatVersion) // file format description
+{
+  mdb_err outErr = 0;
+  if ( outFormatVersion )
+    outFormatVersion->mYarn_Fill = 0;
+  if ( outFilePath )
+    outFilePath->mYarn_Fill = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    if ( mStore_File )
+      mStore_File->Path(mev, outFilePath);
+    else
+      NilStoreFileError(ev);
+    
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetPortFile(
+  nsIMdbEnv* mev, // context
+  nsIMdbFile** acqFile) // acquire file used by port or store
+{
+  mdb_err outErr = 0;
+  if ( acqFile )
+    *acqFile = 0;
+
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    
+    if ( mStore_File )
+    {
+      if ( acqFile )
+      {
+        mStore_File->AddRef();
+        if ( ev->Good() )
+          *acqFile = mStore_File;
+      }
+    }
+    else
+      NilStoreFileError(ev);
+      
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ----- end filepath methods -----
+
+// { ----- begin export methods -----
+NS_IMETHODIMP
+morkStore::BestExportFormat( // determine preferred export format
+  nsIMdbEnv* mev, // context
+  mdbYarn* outFormatVersion) // file format description
+{
+  mdb_err outErr = 0;
+  if ( outFormatVersion )
+    outFormatVersion->mYarn_Fill = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    ev->StubMethodOnlyError();
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::CanExportToFormat( // can export content in given specific format?
+  nsIMdbEnv* mev, // context
+  const char* inFormatVersion, // file format description
+  mdb_bool* outCanExport) // whether ExportSource() might succeed
+{
+  MORK_USED_1(inFormatVersion);
+  mdb_bool canExport = morkBool_kFalse;
+  mdb_err outErr = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    ev->StubMethodOnlyError();
+    outErr = ev->AsErr();
+  }
+  if ( outCanExport )
+    *outCanExport = canExport;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::ExportToFormat( // export content in given specific format
+  nsIMdbEnv* mev, // context
+  // const char* inFilePath, // the file to receive exported content
+  nsIMdbFile* ioFile, // destination abstract file interface
+  const char* inFormatVersion, // file format description
+  nsIMdbThumb** acqThumb) // acquire thumb for incremental export
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the export will be finished.
+{
+  mdb_err outErr = 0;
+  nsIMdbThumb* outThumb = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    if ( ioFile && inFormatVersion && acqThumb )
+    {
+      ev->StubMethodOnlyError();
+    }
+    else
+      ev->NilPointerError();
+    
+    outErr = ev->AsErr();
+  }
+  if ( acqThumb )
+    *acqThumb = outThumb;
+  return outErr;
+}
+
+// } ----- end export methods -----
+
+// { ----- begin token methods -----
+NS_IMETHODIMP
+morkStore::TokenToString( // return a string name for an integer token
+  nsIMdbEnv* mev, // context
+  mdb_token inToken, // token for inTokenName inside this port
+  mdbYarn* outTokenName) // the type of table to access
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    TokenToString(ev, inToken, outTokenName);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::StringToToken( // return an integer token for scope name
+  nsIMdbEnv* mev, // context
+  const char* inTokenName, // Latin1 string to tokenize if possible
+  mdb_token* outToken) // token for inTokenName inside this port
+  // String token zero is never used and never supported. If the port
+  // is a mutable store, then StringToToken() to create a new
+  // association of inTokenName with a new integer token if possible.
+  // But a readonly port will return zero for an unknown scope name.
+{
+  mdb_err outErr = 0;
+  mdb_token token = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    token = StringToToken(ev, inTokenName);
+    outErr = ev->AsErr();
+  }
+  if ( outToken )
+    *outToken = token;
+  return outErr;
+}
+  
+
+NS_IMETHODIMP
+morkStore::QueryToken( // like StringToToken(), but without adding
+  nsIMdbEnv* mev, // context
+  const char* inTokenName, // Latin1 string to tokenize if possible
+  mdb_token* outToken) // token for inTokenName inside this port
+  // QueryToken() will return a string token if one already exists,
+  // but unlike StringToToken(), will not assign a new token if not
+  // already in use.
+{
+  mdb_err outErr = 0;
+  mdb_token token = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    token = QueryToken(ev, inTokenName);
+    outErr = ev->AsErr();
+  }
+  if ( outToken )
+    *outToken = token;
+  return outErr;
+}
+
+
+// } ----- end token methods -----
+
+// { ----- begin row methods -----  
+NS_IMETHODIMP
+morkStore::HasRow( // contains a row with the specified oid?
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid,  // hypothetical row oid
+  mdb_bool* outHasRow) // whether GetRow() might succeed
+{
+  mdb_err outErr = 0;
+  mdb_bool hasRow = morkBool_kFalse;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkRow* row = GetRow(ev, inOid);
+    if ( row )
+      hasRow = morkBool_kTrue;
+      
+    outErr = ev->AsErr();
+  }
+  if ( outHasRow )
+    *outHasRow = hasRow;
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkStore::GetRow( // access one row with specific oid
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid,  // hypothetical row oid
+  nsIMdbRow** acqRow) // acquire specific row (or null)
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkRow* row = GetRow(ev, inOid);
+    if ( row && ev->Good() )
+      outRow = row->AcquireRowHandle(ev, this);
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetRowRefCount( // get number of tables that contain a row 
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid,  // hypothetical row oid
+  mdb_count* outRefCount) // number of tables containing inRowKey 
+{
+  mdb_err outErr = 0;
+  mdb_count count = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkRow* row = GetRow(ev, inOid);
+    if ( row && ev->Good() )
+      count = row->mRow_GcUses;
+      
+    outErr = ev->AsErr();
+  }
+  if ( outRefCount )
+    *outRefCount = count;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::FindRow(nsIMdbEnv* mev, // search for row with matching cell
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_column inColumn,   // the column to search (and maintain an index)
+    const mdbYarn* inTargetCellValue, // cell value for which to search
+    mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match)
+    nsIMdbRow** acqRow) // acquire matching row (or nil for no match)
+  // FindRow() searches for one row that has a cell in column inColumn with
+  // a contained value with the same form (i.e. charset) and is byte-wise
+  // identical to the blob described by yarn inTargetCellValue.  Both content
+  // and form of the yarn must be an exact match to find a matching row.
+  //
+  // (In other words, both a yarn's blob bytes and form are significant.  The
+  // form is not expected to vary in columns used for identity anyway.  This
+  // is intended to make the cost of FindRow() cheaper for MDB implementors,
+  // since any cell value atomization performed internally must necessarily
+  // make yarn form significant in order to avoid data loss in atomization.)
+  //
+  // FindRow() can lazily create an index on attribute inColumn for all rows
+  // with that attribute in row space scope inRowScope, so that subsequent
+  // calls to FindRow() will perform faster.  Such an index might or might
+  // not be persistent (but this seems desirable if it is cheap to do so).
+  // Note that lazy index creation in readonly DBs is not very feasible.
+  //
+  // This FindRow() interface assumes that attribute inColumn is effectively
+  // an alternative means of unique identification for a row in a rowspace,
+  // so correct behavior is only guaranteed when no duplicates for this col
+  // appear in the given set of rows.  (If more than one row has the same cell
+  // value in this column, no more than one will be found; and cutting one of
+  // two duplicate rows can cause the index to assume no other such row lives
+  // in the row space, so future calls return nil for negative search results
+  // even though some duplicate row might still live within the rowspace.)
+  //
+  // In other words, the FindRow() implementation is allowed to assume simple
+  // hash tables mapping unqiue column keys to associated row values will be
+  // sufficient, where any duplication is not recorded because only one copy
+  // of a given key need be remembered.  Implementors are not required to sort
+  // all rows by the specified column.
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  mdbOid rowOid;
+  rowOid.mOid_Scope = 0;
+  rowOid.mOid_Id = (mdb_id) -1;
+  
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkRow* row = FindRow(ev, inRowScope, inColumn, inTargetCellValue);
+    if ( row && ev->Good() )
+    {
+      rowOid = row->mRow_Oid;
+      if ( acqRow )
+          outRow = row->AcquireRowHandle(ev, this);
+    }
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  if ( outRowOid )
+    *outRowOid = rowOid;
+
+  return outErr;
+}
+
+// } ----- end row methods -----
+
+// { ----- begin table methods -----  
+NS_IMETHODIMP
+morkStore::HasTable( // supports a table with the specified oid?
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid,  // hypothetical table oid
+  mdb_bool* outHasTable) // whether GetTable() might succeed
+{
+  mdb_err outErr = 0;
+  mork_bool hasTable = morkBool_kFalse;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkTable* table = GetTable(ev, inOid);
+    if ( table )
+      hasTable = morkBool_kTrue;
+    
+    outErr = ev->AsErr();
+  }
+  if ( outHasTable )
+    *outHasTable = hasTable;
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkStore::GetTable( // access one table with specific oid
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid,  // hypothetical table oid
+  nsIMdbTable** acqTable) // acquire specific table (or null)
+{
+  mdb_err outErr = 0;
+  nsIMdbTable* outTable = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkTable* table = GetTable(ev, inOid);
+    if ( table && ev->Good() )
+      outTable = table->AcquireTableHandle(ev);
+    outErr = ev->AsErr();
+  }
+  if ( acqTable )
+    *acqTable = outTable;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::HasTableKind( // supports a table of the specified type?
+  nsIMdbEnv* mev, // context
+  mdb_scope inRowScope, // rid scope for row ids
+  mdb_kind inTableKind, // the type of table to access
+  mdb_count* outTableCount, // current number of such tables
+  mdb_bool* outSupportsTable) // whether GetTableKind() might succeed
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    *outSupportsTable = HasTableKind(ev, inRowScope,
+        inTableKind, outTableCount);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+      
+NS_IMETHODIMP
+morkStore::GetTableKind( // access one (random) table of specific type
+  nsIMdbEnv* mev, // context
+  mdb_scope inRowScope,      // row scope for row ids
+  mdb_kind inTableKind,      // the type of table to access
+  mdb_count* outTableCount, // current number of such tables
+  mdb_bool* outMustBeUnique, // whether port can hold only one of these
+  nsIMdbTable** acqTable)      // acquire scoped collection of rows
+{
+  mdb_err outErr = 0;
+  nsIMdbTable* outTable = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkTable* table = GetTableKind(ev, inRowScope,
+        inTableKind, outTableCount, outMustBeUnique);
+    if ( table && ev->Good() )
+      outTable = table->AcquireTableHandle(ev);
+    outErr = ev->AsErr();
+  }
+  if ( acqTable )
+    *acqTable = outTable;
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkStore::GetPortTableCursor( // get cursor for all tables of specific type
+  nsIMdbEnv* mev, // context
+  mdb_scope inRowScope, // row scope for row ids
+  mdb_kind inTableKind, // the type of table to access
+  nsIMdbPortTableCursor** acqCursor) // all such tables in the port
+{
+  mdb_err outErr = 0;
+  nsIMdbPortTableCursor* outCursor = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkPortTableCursor* cursor =
+      GetPortTableCursor(ev, inRowScope,
+        inTableKind);
+    if ( cursor && ev->Good() )
+      outCursor = cursor;
+
+    outErr = ev->AsErr();
+  }
+  if ( acqCursor )
+    *acqCursor = outCursor;
+  return outErr;
+}
+// } ----- end table methods -----
+
+// { ----- begin commit methods -----
+  
+NS_IMETHODIMP
+morkStore::ShouldCompress( // store wastes at least inPercentWaste?
+  nsIMdbEnv* mev, // context
+  mdb_percent inPercentWaste, // 0..100 percent file size waste threshold
+  mdb_percent* outActualWaste, // 0..100 percent of file actually wasted
+  mdb_bool* outShould) // true when about inPercentWaste% is wasted
+{
+  mdb_percent actualWaste = 0;
+  mdb_bool shouldCompress = morkBool_kFalse;
+  mdb_err outErr = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    actualWaste = PercentOfStoreWasted(ev);
+    if ( inPercentWaste > 100 )
+      inPercentWaste = 100;
+    shouldCompress = ( actualWaste >= inPercentWaste );
+    outErr = ev->AsErr();
+  }
+  if ( outActualWaste )
+    *outActualWaste = actualWaste;
+  if ( outShould )
+    *outShould = shouldCompress;
+  return outErr;
+}
+
+
+// } ===== end nsIMdbPort methods =====
+
+NS_IMETHODIMP
+morkStore::NewTable( // make one new table of specific type
+  nsIMdbEnv* mev, // context
+  mdb_scope inRowScope,    // row scope for row ids
+  mdb_kind inTableKind,    // the type of table to access
+  mdb_bool inMustBeUnique, // whether store can hold only one of these
+  const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+  nsIMdbTable** acqTable)     // acquire scoped collection of rows
+{
+  mdb_err outErr = 0;
+  nsIMdbTable* outTable = 0;
+  morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkTable* table = NewTable(ev, inRowScope,
+        inTableKind, inMustBeUnique, inOptionalMetaRowOid);
+    if ( table && ev->Good() )
+      outTable = table->AcquireTableHandle(ev);
+    outErr = ev->AsErr();
+  }
+  if ( acqTable )
+    *acqTable = outTable;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::NewTableWithOid( // make one new table of specific type
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid,   // caller assigned oid
+  mdb_kind inTableKind,    // the type of table to access
+  mdb_bool inMustBeUnique, // whether store can hold only one of these
+  const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+  nsIMdbTable** acqTable)     // acquire scoped collection of rows
+{
+  mdb_err outErr = 0;
+  nsIMdbTable* outTable = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkTable* table = OidToTable(ev, inOid,
+      inOptionalMetaRowOid);
+    if ( table && ev->Good() )
+    {
+      table->mTable_Kind = inTableKind;
+      if ( inMustBeUnique )
+        table->SetTableUnique();
+      outTable = table->AcquireTableHandle(ev);
+    }
+    outErr = ev->AsErr();
+  }
+  if ( acqTable )
+    *acqTable = outTable;
+  return outErr;
+}
+// } ----- end table methods -----
+
+// { ----- begin row scope methods -----
+NS_IMETHODIMP
+morkStore::RowScopeHasAssignedIds(nsIMdbEnv* mev,
+  mdb_scope inRowScope,   // row scope for row ids
+  mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+  mdb_bool* outStoreAssigned) // nonzero if store db assigned specified
+{
+  NS_ASSERTION(PR_FALSE, " not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStore::SetCallerAssignedIds(nsIMdbEnv* mev,
+  mdb_scope inRowScope,   // row scope for row ids
+  mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+  mdb_bool* outStoreAssigned) // nonzero if store db assigned specified
+{
+  NS_ASSERTION(PR_FALSE, " not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStore::SetStoreAssignedIds(nsIMdbEnv* mev,
+  mdb_scope inRowScope,   // row scope for row ids
+  mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+  mdb_bool* outStoreAssigned) // nonzero if store db assigned specified
+{
+  NS_ASSERTION(PR_FALSE, " not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end row scope methods -----
+
+// { ----- begin row methods -----
+NS_IMETHODIMP
+morkStore::NewRowWithOid(nsIMdbEnv* mev, // new row w/ caller assigned oid
+  const mdbOid* inOid,   // caller assigned oid
+  nsIMdbRow** acqRow) // create new row
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkRow* row = NewRowWithOid(ev, inOid);
+    if ( row && ev->Good() )
+      outRow = row->AcquireRowHandle(ev, this);
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::NewRow(nsIMdbEnv* mev, // new row with db assigned oid
+  mdb_scope inRowScope,   // row scope for row ids
+  nsIMdbRow** acqRow) // create new row
+// Note this row must be added to some table or cell child before the
+// store is closed in order to make this row persist across sesssions.
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    morkRow* row = NewRow(ev, inRowScope);
+    if ( row && ev->Good() )
+      outRow = row->AcquireRowHandle(ev, this);
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+// } ----- end row methods -----
+
+// { ----- begin inport/export methods -----
+NS_IMETHODIMP
+morkStore::ImportContent( // import content from port
+  nsIMdbEnv* mev, // context
+  mdb_scope inRowScope, // scope for rows (or zero for all?)
+  nsIMdbPort* ioPort, // the port with content to add to store
+  nsIMdbThumb** acqThumb) // acquire thumb for incremental import
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the import will be finished.
+{
+  NS_ASSERTION(PR_FALSE, " not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStore::ImportFile( // import content from port
+  nsIMdbEnv* mev, // context
+  nsIMdbFile* ioFile, // the file with content to add to store
+  nsIMdbThumb** acqThumb) // acquire thumb for incremental import
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the import will be finished.
+{
+  NS_ASSERTION(PR_FALSE, " not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end inport/export methods -----
+
+// { ----- begin hinting methods -----
+NS_IMETHODIMP
+morkStore::ShareAtomColumnsHint( // advise re shared col content atomizing
+  nsIMdbEnv* mev, // context
+  mdb_scope inScopeHint, // zero, or suggested shared namespace
+  const mdbColumnSet* inColumnSet) // cols desired tokenized together
+{
+  MORK_USED_2(inColumnSet,inScopeHint);
+  mdb_err outErr = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    // ev->StubMethodOnlyError(); // okay to do nothing for a hint method
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::AvoidAtomColumnsHint( // advise col w/ poor atomizing prospects
+  nsIMdbEnv* mev, // context
+  const mdbColumnSet* inColumnSet) // cols with poor atomizing prospects
+{
+  MORK_USED_1(inColumnSet);
+  mdb_err outErr = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    // ev->StubMethodOnlyError(); // okay to do nothing for a hint method
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ----- end hinting methods -----
+
+// { ----- begin commit methods -----
+NS_IMETHODIMP
+morkStore::SmallCommit( // save minor changes if convenient and uncostly
+  nsIMdbEnv* mev) // context
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    // ev->StubMethodOnlyError(); // it's okay to do nothing for small commit
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::LargeCommit( // save important changes if at all possible
+  nsIMdbEnv* mev, // context
+  nsIMdbThumb** acqThumb) // acquire thumb for incremental commit
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the commit will be finished.  Note the store is effectively write
+// locked until commit is finished or canceled through the thumb instance.
+// Until the commit is done, the store will report it has readonly status.
+{
+  nsresult outErr = 0;
+  nsIMdbThumb* outThumb = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    
+    morkThumb* thumb = 0;
+    // morkFile* file = store->mStore_File;
+    if ( DoPreferLargeOverCompressCommit(ev) )
+    {
+      thumb = morkThumb::Make_LargeCommit(ev, mPort_Heap, this);
+    }
+    else
+    {
+      mork_bool doCollect = morkBool_kFalse;
+      thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect);
+    }
+    
+    if ( thumb )
+    {
+      outThumb = thumb;
+      thumb->AddRef();
+    }
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqThumb )
+    *acqThumb = outThumb;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::SessionCommit( // save all changes if large commits delayed
+  nsIMdbEnv* mev, // context
+  nsIMdbThumb** acqThumb) // acquire thumb for incremental commit
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the commit will be finished.  Note the store is effectively write
+// locked until commit is finished or canceled through the thumb instance.
+// Until the commit is done, the store will report it has readonly status.
+{
+  nsresult outErr = NS_OK;
+  nsIMdbThumb* outThumb = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    
+    morkThumb* thumb = 0;
+    if ( DoPreferLargeOverCompressCommit(ev) )
+    {
+      thumb = morkThumb::Make_LargeCommit(ev, mPort_Heap, this);
+    }
+    else
+    {
+      mork_bool doCollect = morkBool_kFalse;
+      thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect);
+    }
+    
+    if ( thumb )
+    {
+      outThumb = thumb;
+      thumb->AddRef();
+    }
+    outErr = ev->AsErr();
+  }
+  if ( acqThumb )
+    *acqThumb = outThumb;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::CompressCommit( // commit and make db smaller if possible
+  nsIMdbEnv* mev, // context
+  nsIMdbThumb** acqThumb) // acquire thumb for incremental commit
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the commit will be finished.  Note the store is effectively write
+// locked until commit is finished or canceled through the thumb instance.
+// Until the commit is done, the store will report it has readonly status.
+{
+  nsresult outErr = NS_OK;
+  nsIMdbThumb* outThumb = 0;
+  morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+  if ( ev )
+  {
+    mork_bool doCollect = morkBool_kFalse;
+    morkThumb* thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect);
+    if ( thumb )
+    {
+      outThumb = thumb;
+      thumb->AddRef();
+      mStore_CanWriteIncremental = morkBool_kTrue;
+    }
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqThumb )
+    *acqThumb = outThumb;
+  return outErr;
+}
+
+// } ----- end commit methods -----
+
+// } ===== end nsIMdbStore methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkStore.h
@@ -0,0 +1,794 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKSTORE_
+#define _MORKSTORE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKZONE_
+#include "morkZone.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kPort  /*i*/ 0x7054 /* ascii 'pT' */
+
+#define morkDerived_kStore  /*i*/ 0x7354 /* ascii 'sT' */
+
+/*| kGroundColumnSpace: we use the 'column space' as the default scope
+**| for grounding column name IDs, and this is also the default scope for
+**| all other explicitly tokenized strings.
+|*/
+#define morkStore_kGroundColumnSpace 'c' /* for mStore_GroundColumnSpace*/
+#define morkStore_kColumnSpaceScope ((mork_scope) 'c') /*kGroundColumnSpace*/
+#define morkStore_kValueSpaceScope ((mork_scope) 'v')
+#define morkStore_kStreamBufSize (8 * 1024) /* okay buffer size */
+
+#define morkStore_kReservedColumnCount 0x20 /* for well-known columns */
+
+#define morkStore_kNoneToken ((mork_token) 'n')
+#define morkStore_kFormColumn ((mork_column) 'f')
+#define morkStore_kAtomScopeColumn ((mork_column) 'a')
+#define morkStore_kRowScopeColumn ((mork_column) 'r')
+#define morkStore_kMetaScope ((mork_scope) 'm')
+#define morkStore_kKindColumn ((mork_column) 'k')
+#define morkStore_kStatusColumn ((mork_column) 's')
+
+/*| morkStore: 
+|*/
+class morkStore :  public morkObject, public nsIMdbStore {
+
+public: // state is public because the entire Mork system is private
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  morkEnv*        mPort_Env;      // non-refcounted env which created port
+  morkFactory*    mPort_Factory;  // weak ref to suite factory
+  nsIMdbHeap*     mPort_Heap;     // heap in which this port allocs objects
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  
+  void ClosePort(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+  mork_bool IsPort() const
+  { return IsNode() && mNode_Derived == morkDerived_kPort; }
+// } ===== end morkNode methods =====
+
+public: // other port methods
+
+  // { ----- begin attribute methods -----
+//  NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly);
+  // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+  // } ----- end attribute methods -----
+
+  // { ----- begin factory methods -----
+//  NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory); 
+  // } ----- end factory methods -----
+
+  // { ----- begin ref counting for well-behaved cyclic graphs -----
+  NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
+    mdb_count* outCount);  
+  NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
+    mdb_count* outCount);
+
+  NS_IMETHOD AddWeakRef(nsIMdbEnv* ev);
+  NS_IMETHOD AddStrongRef(nsIMdbEnv* ev);
+
+  NS_IMETHOD CutWeakRef(nsIMdbEnv* ev);
+  NS_IMETHOD CutStrongRef(nsIMdbEnv* ev);
+  
+  NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev); // called at strong refs zero
+  NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen);
+  // } ----- end ref counting -----
+  
+// } ===== end nsIMdbObject methods =====
+
+// { ===== begin nsIMdbPort methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool);
+  NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool);
+  NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool);
+
+  NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev, 
+    mdbUsagePolicy* ioUsagePolicy);
+
+  NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev, 
+    const mdbUsagePolicy* inUsagePolicy);
+  // } ----- end attribute methods -----
+
+  // { ----- begin memory policy methods -----  
+  NS_IMETHOD IdleMemoryPurge( // do memory management already scheduled
+    nsIMdbEnv* ev, // context
+    mdb_size* outEstimatedBytesFreed); // approximate bytes actually freed
+
+  NS_IMETHOD SessionMemoryPurge( // request specific footprint decrease
+    nsIMdbEnv* ev, // context
+    mdb_size inDesiredBytesFreed, // approximate number of bytes wanted
+    mdb_size* outEstimatedBytesFreed); // approximate bytes actually freed
+
+  NS_IMETHOD PanicMemoryPurge( // desperately free all possible memory
+    nsIMdbEnv* ev, // context
+    mdb_size* outEstimatedBytesFreed); // approximate bytes actually freed
+  // } ----- end memory policy methods -----
+
+  // { ----- begin filepath methods -----
+  NS_IMETHOD GetPortFilePath(
+    nsIMdbEnv* ev, // context
+    mdbYarn* outFilePath, // name of file holding port content
+    mdbYarn* outFormatVersion); // file format description
+
+  NS_IMETHOD GetPortFile(
+    nsIMdbEnv* ev, // context
+    nsIMdbFile** acqFile); // acquire file used by port or store
+  // } ----- end filepath methods -----
+
+  // { ----- begin export methods -----
+  NS_IMETHOD BestExportFormat( // determine preferred export format
+    nsIMdbEnv* ev, // context
+    mdbYarn* outFormatVersion); // file format description
+
+  NS_IMETHOD
+  CanExportToFormat( // can export content in given specific format?
+    nsIMdbEnv* ev, // context
+    const char* inFormatVersion, // file format description
+    mdb_bool* outCanExport); // whether ExportSource() might succeed
+
+  NS_IMETHOD ExportToFormat( // export content in given specific format
+    nsIMdbEnv* ev, // context
+    // const char* inFilePath, // the file to receive exported content
+    nsIMdbFile* ioFile, // destination abstract file interface
+    const char* inFormatVersion, // file format description
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental export
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the export will be finished.
+
+  // } ----- end export methods -----
+
+  // { ----- begin token methods -----
+  NS_IMETHOD TokenToString( // return a string name for an integer token
+    nsIMdbEnv* ev, // context
+    mdb_token inToken, // token for inTokenName inside this port
+    mdbYarn* outTokenName); // the type of table to access
+  
+  NS_IMETHOD StringToToken( // return an integer token for scope name
+    nsIMdbEnv* ev, // context
+    const char* inTokenName, // Latin1 string to tokenize if possible
+    mdb_token* outToken); // token for inTokenName inside this port
+    
+  // String token zero is never used and never supported. If the port
+  // is a mutable store, then StringToToken() to create a new
+  // association of inTokenName with a new integer token if possible.
+  // But a readonly port will return zero for an unknown scope name.
+
+  NS_IMETHOD QueryToken( // like StringToToken(), but without adding
+    nsIMdbEnv* ev, // context
+    const char* inTokenName, // Latin1 string to tokenize if possible
+    mdb_token* outToken); // token for inTokenName inside this port
+  
+  // QueryToken() will return a string token if one already exists,
+  // but unlike StringToToken(), will not assign a new token if not
+  // already in use.
+
+  // } ----- end token methods -----
+
+  // { ----- begin row methods -----  
+  NS_IMETHOD HasRow( // contains a row with the specified oid?
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical row oid
+    mdb_bool* outHasRow); // whether GetRow() might succeed
+
+  NS_IMETHOD GetRowRefCount( // get number of tables that contain a row 
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical row oid
+    mdb_count* outRefCount); // number of tables containing inRowKey 
+    
+  NS_IMETHOD GetRow( // access one row with specific oid
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical row oid
+    nsIMdbRow** acqRow); // acquire specific row (or null)
+
+  NS_IMETHOD FindRow(nsIMdbEnv* ev, // search for row with matching cell
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_column inColumn,   // the column to search (and maintain an index)
+    const mdbYarn* inTargetCellValue, // cell value for which to search
+    mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match)
+    nsIMdbRow** acqRow); // acquire matching row (or nil for no match)
+                         // can be null if you only want the oid
+  // FindRow() searches for one row that has a cell in column inColumn with
+  // a contained value with the same form (i.e. charset) and is byte-wise
+  // identical to the blob described by yarn inTargetCellValue.  Both content
+  // and form of the yarn must be an exact match to find a matching row.
+  //
+  // (In other words, both a yarn's blob bytes and form are significant.  The
+  // form is not expected to vary in columns used for identity anyway.  This
+  // is intended to make the cost of FindRow() cheaper for MDB implementors,
+  // since any cell value atomization performed internally must necessarily
+  // make yarn form significant in order to avoid data loss in atomization.)
+  //
+  // FindRow() can lazily create an index on attribute inColumn for all rows
+  // with that attribute in row space scope inRowScope, so that subsequent
+  // calls to FindRow() will perform faster.  Such an index might or might
+  // not be persistent (but this seems desirable if it is cheap to do so).
+  // Note that lazy index creation in readonly DBs is not very feasible.
+  //
+  // This FindRow() interface assumes that attribute inColumn is effectively
+  // an alternative means of unique identification for a row in a rowspace,
+  // so correct behavior is only guaranteed when no duplicates for this col
+  // appear in the given set of rows.  (If more than one row has the same cell
+  // value in this column, no more than one will be found; and cutting one of
+  // two duplicate rows can cause the index to assume no other such row lives
+  // in the row space, so future calls return nil for negative search results
+  // even though some duplicate row might still live within the rowspace.)
+  //
+  // In other words, the FindRow() implementation is allowed to assume simple
+  // hash tables mapping unqiue column keys to associated row values will be
+  // sufficient, where any duplication is not recorded because only one copy
+  // of a given key need be remembered.  Implementors are not required to sort
+  // all rows by the specified column.
+  // } ----- end row methods -----
+
+  // { ----- begin table methods -----  
+  NS_IMETHOD HasTable( // supports a table with the specified oid?
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical table oid
+    mdb_bool* outHasTable); // whether GetTable() might succeed
+    
+  NS_IMETHOD GetTable( // access one table with specific oid
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // hypothetical table oid
+    nsIMdbTable** acqTable); // acquire specific table (or null)
+  
+  NS_IMETHOD HasTableKind( // supports a table of the specified type?
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope, // rid scope for row ids
+    mdb_kind inTableKind, // the type of table to access
+    mdb_count* outTableCount, // current number of such tables
+    mdb_bool* outSupportsTable); // whether GetTableKind() might succeed
+        
+  NS_IMETHOD GetTableKind( // access one (random) table of specific type
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope,      // row scope for row ids
+    mdb_kind inTableKind,      // the type of table to access
+    mdb_count* outTableCount, // current number of such tables
+    mdb_bool* outMustBeUnique, // whether port can hold only one of these
+    nsIMdbTable** acqTable);       // acquire scoped collection of rows
+    
+  NS_IMETHOD
+  GetPortTableCursor( // get cursor for all tables of specific type
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope, // row scope for row ids
+    mdb_kind inTableKind, // the type of table to access
+    nsIMdbPortTableCursor** acqCursor); // all such tables in the port
+  // } ----- end table methods -----
+
+
+  // { ----- begin commit methods -----
+
+  NS_IMETHOD ShouldCompress( // store wastes at least inPercentWaste?
+    nsIMdbEnv* ev, // context
+    mdb_percent inPercentWaste, // 0..100 percent file size waste threshold
+    mdb_percent* outActualWaste, // 0..100 percent of file actually wasted
+    mdb_bool* outShould); // true when about inPercentWaste% is wasted
+  // ShouldCompress() returns true if the store can determine that the file
+  // will shrink by an estimated percentage of inPercentWaste% (or more) if
+  // CompressCommit() is called, because that percentage of the file seems
+  // to be recoverable free space.  The granularity is only in terms of 
+  // percentage points, and any value over 100 is considered equal to 100.
+  //
+  // If a store only has an approximate idea how much space might be saved
+  // during a compress, then a best guess should be made.  For example, the
+  // Mork implementation might keep track of how much file space began with
+  // text content before the first updating transaction, and then consider
+  // all content following the start of the first transaction as potentially
+  // wasted space if it is all updates and not just new content.  (This is
+  // a safe assumption in the sense that behavior will stabilize on a low
+  // estimate of wastage after a commit removes all transaction updates.)
+  //
+  // Some db formats might attempt to keep a very accurate reckoning of free
+  // space size, so a very accurate determination can be made.  But other db
+  // formats might have difficulty determining size of free space, and might
+  // require some lengthy calculation to answer.  This is the reason for
+  // passing in the percentage threshold of interest, so that such lengthy
+  // computations can terminate early as soon as at least inPercentWaste is
+  // found, so that the entire file need not be groveled when unnecessary.
+  // However, we hope implementations will always favor fast but imprecise
+  // heuristic answers instead of extremely slow but very precise answers.
+  //
+  // If the outActualWaste parameter is non-nil, it will be used to return
+  // the actual estimated space wasted as a percentage of file size.  (This
+  // parameter is provided so callers need not call repeatedly with altered
+  // inPercentWaste values to isolate the actual wastage figure.)  Note the
+  // actual wastage figure returned can exactly equal inPercentWaste even
+  // when this grossly underestimates the real figure involved, if the db
+  // finds it very expensive to determine the extent of wastage after it is
+  // known to at least exceed inPercentWaste.  Note we expect that whenever
+  // outShould returns true, that outActualWaste returns >= inPercentWaste.
+  //
+  // The effect of different inPercentWaste values is not very uniform over
+  // the permitted range.  For example, 50 represents 50% wastage, or a file
+  // that is about double what it should be ideally.  But 99 represents 99%
+  // wastage, or a file that is about ninety-nine times as big as it should
+  // be ideally.  In the smaller direction, 25 represents 25% wastage, or
+  // a file that is only 33% larger than it should be ideally.
+  //
+  // Callers can determine what policy they want to use for considering when
+  // a file holds too much wasted space, and express this as a percentage
+  // of total file size to pass as in the inPercentWaste parameter.  A zero
+  // likely returns always trivially true, and 100 always trivially false.
+  // The great majority of callers are expected to use values from 25 to 75,
+  // since most plausible thresholds for compressing might fall between the
+  // extremes of 133% of ideal size and 400% of ideal size.  (Presumably the
+  // larger a file gets, the more important the percentage waste involved, so
+  // a sliding scale for compress thresholds might use smaller numbers for
+  // much bigger file sizes.)
+  
+  // } ----- end commit methods -----
+
+// } ===== end nsIMdbPort methods =====
+
+// { ===== begin nsIMdbStore methods =====
+
+  // { ----- begin table methods -----
+  NS_IMETHOD NewTable( // make one new table of specific type
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope,    // row scope for row ids
+    mdb_kind inTableKind,    // the type of table to access
+    mdb_bool inMustBeUnique, // whether store can hold only one of these
+    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+    nsIMdbTable** acqTable);     // acquire scoped collection of rows
+    
+  NS_IMETHOD NewTableWithOid( // make one new table of specific type
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,   // caller assigned oid
+    mdb_kind inTableKind,    // the type of table to access
+    mdb_bool inMustBeUnique, // whether store can hold only one of these
+    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+    nsIMdbTable** acqTable);     // acquire scoped collection of rows
+  // } ----- end table methods -----
+
+  // { ----- begin row scope methods -----
+  NS_IMETHOD RowScopeHasAssignedIds(nsIMdbEnv* ev,
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+    mdb_bool* outStoreAssigned); // nonzero if store db assigned specified
+
+  NS_IMETHOD SetCallerAssignedIds(nsIMdbEnv* ev,
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+    mdb_bool* outStoreAssigned); // nonzero if store db assigned specified
+
+  NS_IMETHOD SetStoreAssignedIds(nsIMdbEnv* ev,
+    mdb_scope inRowScope,   // row scope for row ids
+    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+    mdb_bool* outStoreAssigned); // nonzero if store db assigned specified
+  // } ----- end row scope methods -----
+
+  // { ----- begin row methods -----
+  NS_IMETHOD NewRowWithOid(nsIMdbEnv* ev, // new row w/ caller assigned oid
+    const mdbOid* inOid,   // caller assigned oid
+    nsIMdbRow** acqRow); // create new row
+
+  NS_IMETHOD NewRow(nsIMdbEnv* ev, // new row with db assigned oid
+    mdb_scope inRowScope,   // row scope for row ids
+    nsIMdbRow** acqRow); // create new row
+  // Note this row must be added to some table or cell child before the
+  // store is closed in order to make this row persist across sesssions.
+
+  // } ----- end row methods -----
+
+  // { ----- begin inport/export methods -----
+  NS_IMETHOD ImportContent( // import content from port
+    nsIMdbEnv* ev, // context
+    mdb_scope inRowScope, // scope for rows (or zero for all?)
+    nsIMdbPort* ioPort, // the port with content to add to store
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental import
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the import will be finished.
+
+  NS_IMETHOD ImportFile( // import content from port
+    nsIMdbEnv* ev, // context
+    nsIMdbFile* ioFile, // the file with content to add to store
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental import
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the import will be finished.
+  // } ----- end inport/export methods -----
+
+  // { ----- begin hinting methods -----
+  NS_IMETHOD
+  ShareAtomColumnsHint( // advise re shared column content atomizing
+    nsIMdbEnv* ev, // context
+    mdb_scope inScopeHint, // zero, or suggested shared namespace
+    const mdbColumnSet* inColumnSet); // cols desired tokenized together
+
+  NS_IMETHOD
+  AvoidAtomColumnsHint( // advise column with poor atomizing prospects
+    nsIMdbEnv* ev, // context
+    const mdbColumnSet* inColumnSet); // cols with poor atomizing prospects
+  // } ----- end hinting methods -----
+
+  // { ----- begin commit methods -----
+  NS_IMETHOD SmallCommit( // save minor changes if convenient and uncostly
+    nsIMdbEnv* ev); // context
+  
+  NS_IMETHOD LargeCommit( // save important changes if at all possible
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental commit
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the commit will be finished.  Note the store is effectively write
+  // locked until commit is finished or canceled through the thumb instance.
+  // Until the commit is done, the store will report it has readonly status.
+
+  NS_IMETHOD SessionCommit( // save all changes if large commits delayed
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental commit
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the commit will be finished.  Note the store is effectively write
+  // locked until commit is finished or canceled through the thumb instance.
+  // Until the commit is done, the store will report it has readonly status.
+
+  NS_IMETHOD
+  CompressCommit( // commit and make db physically smaller if possible
+    nsIMdbEnv* ev, // context
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental commit
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the commit will be finished.  Note the store is effectively write
+  // locked until commit is finished or canceled through the thumb instance.
+  // Until the commit is done, the store will report it has readonly status.
+  
+  // } ----- end commit methods -----
+
+// } ===== end nsIMdbStore methods =====
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakPort(morkPort* me,
+    morkEnv* ev, morkPort** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongPort(morkPort* me,
+    morkEnv* ev, morkPort** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+// public: // slots inherited from morkPort (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+ 
+  // morkEnv*        mPort_Env;      // non-refcounted env which created port
+  // morkFactory*    mPort_Factory;  // weak ref to suite factory
+  // nsIMdbHeap*     mPort_Heap;     // heap in which this port allocs objects
+
+public: // state is public because the entire Mork system is private
+
+// mStore_OidAtomSpace might be unnecessary; I don't remember why I wanted it.
+  morkAtomSpace*   mStore_OidAtomSpace;   // ground atom space for oids
+  morkAtomSpace*   mStore_GroundAtomSpace; // ground atom space for scopes
+  morkAtomSpace*   mStore_GroundColumnSpace; // ground column space for scopes
+
+  nsIMdbFile*      mStore_File; // the file containing Mork text
+  morkStream*      mStore_InStream; // stream using file used by the builder
+  morkBuilder*     mStore_Builder; // to parse Mork text and build structures 
+
+  morkStream*      mStore_OutStream; // stream using file used by the writer
+  
+  morkRowSpaceMap  mStore_RowSpaces;  // maps mork_scope -> morkSpace
+  morkAtomSpaceMap mStore_AtomSpaces; // maps mork_scope -> morkSpace
+  
+  morkZone         mStore_Zone;
+  
+  morkPool         mStore_Pool;
+
+  // we alloc a max size book atom to reuse space for atom map key searches:
+  // morkMaxBookAtom  mStore_BookAtom; // staging area for atom map searches
+  
+  morkFarBookAtom  mStore_FarBookAtom; // staging area for atom map searches
+  
+  // GroupIdentity should be one more than largest seen in a parsed db file:
+  mork_gid         mStore_CommitGroupIdentity; // transaction ID number
+  
+  // group positions are used to help compute PercentOfStoreWasted():
+  mork_pos         mStore_FirstCommitGroupPos; // start of first group
+  mork_pos         mStore_SecondCommitGroupPos; // start of second group
+  // If the first commit group is very near the start of the file (say less
+  // than 512 bytes), then we might assume the file started nearly empty and
+  // that most of the first group is not wasted.  In that case, the pos of
+  // the second commit group might make a better estimate of the start of
+  // transaction space that might represent wasted file space.  That's why
+  // we support fields for both first and second commit group positions.
+  //
+  // We assume that a zero in either group pos means that the slot has not
+  // yet been given a valid value, since the file will always start with a
+  // tag, and a commit group cannot actually start at position zero.
+  //
+  // Either or both the first or second commit group positions might be
+  // supplied by either morkWriter (while committing) or morkBuilder (while
+  // parsing), since either reading or writing the file might encounter the
+  // first transaction groups which came into existence either in the past
+  // or in the very recent present.
+  
+  mork_bool        mStore_CanAutoAssignAtomIdentity;
+  mork_bool        mStore_CanDirty; // changes imply the store becomes dirty?
+  mork_u1          mStore_CanWriteIncremental; // compress not required?
+  mork_u1          mStore_Pad; // for u4 alignment
+  
+  // mStore_CanDirty should be FALSE when parsing a file while building the
+  // content going into the store, because such data structure modifications
+  // are actuallly in sync with the file.  So content read from a file must
+  // be clean with respect to the file.  After a file is finished parsing,
+  // the mStore_CanDirty slot should become TRUE, so that any additional
+  // changes at runtime cause structures to be marked dirty with respect to
+  // the file which must later be updated with changes during a commit.
+  //
+  // It might also make sense to set mStore_CanDirty to FALSE while a commit
+  // is in progress, lest some internal transformations make more content
+  // appear dirty when it should not.  So anyone modifying content during a
+  // commit should think about the intended significance regarding dirty.
+
+public: // more specific dirty methods for store:
+  void SetStoreDirty() { this->SetNodeDirty(); }
+  void SetStoreClean() { this->SetNodeClean(); }
+  
+  mork_bool IsStoreClean() const { return this->IsNodeClean(); }
+  mork_bool IsStoreDirty() const { return this->IsNodeDirty(); }
+ 
+public: // setting dirty based on CanDirty:
+ 
+  void MaybeDirtyStore()
+  { if ( mStore_CanDirty ) this->SetStoreDirty(); }
+  
+public: // space waste analysis
+
+  mork_percent PercentOfStoreWasted(morkEnv* ev);
+ 
+public: // setting store and all subspaces canDirty:
+ 
+  void SetStoreAndAllSpacesCanDirty(morkEnv* ev, mork_bool inCanDirty);
+
+public: // building an atom inside mStore_FarBookAtom from a char* string
+
+  morkFarBookAtom* StageAliasAsFarBookAtom(morkEnv* ev,
+    const morkMid* inMid, morkAtomSpace* ioSpace, mork_cscode inForm);
+
+  morkFarBookAtom* StageYarnAsFarBookAtom(morkEnv* ev,
+    const mdbYarn* inYarn, morkAtomSpace* ioSpace);
+
+  morkFarBookAtom* StageStringAsFarBookAtom(morkEnv* ev,
+    const char* inString, mork_cscode inForm, morkAtomSpace* ioSpace);
+
+public: // determining whether incremental writing is a good use of time:
+
+  mork_bool DoPreferLargeOverCompressCommit(morkEnv* ev);
+  // true when mStore_CanWriteIncremental && store has file large enough 
+
+public: // lazy creation of members and nested row or atom spaces
+
+  morkAtomSpace*   LazyGetOidAtomSpace(morkEnv* ev);
+  morkAtomSpace*   LazyGetGroundAtomSpace(morkEnv* ev);
+  morkAtomSpace*   LazyGetGroundColumnSpace(morkEnv* ev);
+
+  morkStream*      LazyGetInStream(morkEnv* ev);
+  morkBuilder*     LazyGetBuilder(morkEnv* ev); 
+  void             ForgetBuilder(morkEnv* ev); 
+
+  morkStream*      LazyGetOutStream(morkEnv* ev);
+  
+  morkRowSpace*    LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope);
+  morkAtomSpace*   LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope);
+ 
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseStore() only if open
+  virtual ~morkStore(); // assert that CloseStore() executed earlier
+  
+public: // morkStore construction & destruction
+  morkStore(morkEnv* ev, const morkUsage& inUsage,
+     nsIMdbHeap* ioNodeHeap, // the heap (if any) for this node instance
+     morkFactory* inFactory, // the factory for this
+     nsIMdbHeap* ioPortHeap  // the heap to hold all content in the port
+     );
+  void CloseStore(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkStore(const morkStore& other);
+  morkStore& operator=(const morkStore& other);
+
+public: // dynamic type identification
+  morkEnv*  CanUseStore(nsIMdbEnv* mev, mork_bool inMutable, mdb_err* outErr) const;
+   mork_bool IsStore() const
+  { return IsNode() && mNode_Derived == morkDerived_kStore; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonStoreTypeError(morkEnv* ev);
+  static void NilStoreFileError(morkEnv* ev);
+  static void CannotAutoAssignAtomIdentityError(morkEnv* ev);
+  
+public: //  store utilties
+  
+  morkAtom* YarnToAtom(morkEnv* ev, const mdbYarn* inYarn, PRBool createIfMissing = PR_TRUE);
+  morkAtom* AddAlias(morkEnv* ev, const morkMid& inMid,
+    mork_cscode inForm);
+
+public: // other store methods
+
+  void RenumberAllCollectableContent(morkEnv* ev);
+
+  nsIMdbStore* AcquireStoreHandle(morkEnv* ev); // mObject_Handle
+
+  morkPool* StorePool() { return &mStore_Pool; }
+
+  mork_bool OpenStoreFile(morkEnv* ev, // return value equals ev->Good()
+    mork_bool inFrozen,
+    // const char* inFilePath,
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy);
+
+  mork_bool CreateStoreFile(morkEnv* ev, // return value equals ev->Good()
+    // const char* inFilePath,
+    nsIMdbFile* ioFile, // db abstract file interface
+    const mdbOpenPolicy* inOpenPolicy);
+    
+  morkAtom* CopyAtom(morkEnv* ev, const morkAtom* inAtom);
+  // copy inAtom (from some other store) over to this store
+    
+  mork_token CopyToken(morkEnv* ev, mdb_token inToken, morkStore* inStore);
+  // copy inToken from inStore over to this store
+    
+  mork_token BufToToken(morkEnv* ev, const morkBuf* inBuf);
+  mork_token StringToToken(morkEnv* ev, const char* inTokenName);
+  mork_token QueryToken(morkEnv* ev, const char* inTokenName);
+  void TokenToString(morkEnv* ev, mdb_token inToken, mdbYarn* outTokenName);
+  
+  mork_bool MidToOid(morkEnv* ev, const morkMid& inMid,
+     mdbOid* outOid);
+  mork_bool OidToYarn(morkEnv* ev, const mdbOid& inOid, mdbYarn* outYarn);
+  mork_bool MidToYarn(morkEnv* ev, const morkMid& inMid,
+     mdbYarn* outYarn);
+
+  morkBookAtom* MidToAtom(morkEnv* ev, const morkMid& inMid);
+  morkRow* MidToRow(morkEnv* ev, const morkMid& inMid);
+  morkTable* MidToTable(morkEnv* ev, const morkMid& inMid);
+  
+  morkRow* OidToRow(morkEnv* ev, const mdbOid* inOid);
+  // OidToRow() finds old row with oid, or makes new one if not found.
+
+  morkTable* OidToTable(morkEnv* ev, const mdbOid* inOid,
+    const mdbOid* inOptionalMetaRowOid);
+  // OidToTable() finds old table with oid, or makes new one if not found.
+  
+  static void SmallTokenToOneByteYarn(morkEnv* ev, mdb_token inToken,
+    mdbYarn* outYarn);
+  
+  mork_bool HasTableKind(morkEnv* ev, mdb_scope inRowScope, 
+    mdb_kind inTableKind, mdb_count* outTableCount);
+  
+  morkTable* GetTableKind(morkEnv* ev, mdb_scope inRowScope, 
+    mdb_kind inTableKind, mdb_count* outTableCount,
+    mdb_bool* outMustBeUnique);
+
+  morkRow* FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn,
+    const mdbYarn* inTargetCellValue);
+  
+  morkRow* GetRow(morkEnv* ev, const mdbOid* inOid);
+  morkTable* GetTable(morkEnv* ev, const mdbOid* inOid);
+    
+  morkTable* NewTable(morkEnv* ev, mdb_scope inRowScope,
+    mdb_kind inTableKind, mdb_bool inMustBeUnique,
+    const mdbOid* inOptionalMetaRowOid);
+
+  morkPortTableCursor* GetPortTableCursor(morkEnv* ev, mdb_scope inRowScope,
+    mdb_kind inTableKind) ;
+
+  morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid);
+  morkRow* NewRow(morkEnv* ev, mdb_scope inRowScope);
+
+  morkThumb* MakeCompressCommitThumb(morkEnv* ev, mork_bool inDoCollect);
+
+public: // commit related methods
+
+  mork_bool MarkAllStoreContentDirty(morkEnv* ev);
+  // MarkAllStoreContentDirty() visits every object in the store and marks 
+  // them dirty, including every table, row, cell, and atom.  The return
+  // equals ev->Good(), to show whether any error happened.  This method is
+  // intended for use in the beginning of a "compress commit" which writes
+  // all store content, whether dirty or not.  We dirty everything first so
+  // that later iterations over content can mark things clean as they are
+  // written, and organize the process of serialization so that objects are
+  // written only at need (because of being dirty).
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakStore(morkStore* me,
+    morkEnv* ev, morkStore** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongStore(morkStore* me,
+    morkEnv* ev, morkStore** ioSlot)
+  { 
+    morkStore* store = *ioSlot;
+    if ( me != store )
+    {
+      if ( store )
+      {
+        // what if this nulls out the ev and causes asserts?
+        // can we move this after the CutStrongRef()?
+        *ioSlot = 0;
+        store->Release();
+      }
+      if ( me && me->AddRef() )
+        *ioSlot = me;
+    }
+  }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSTORE_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkStream.cpp
@@ -0,0 +1,896 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKFILE_
+#include "morkFile.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkStream::CloseMorkNode(morkEnv* ev) // CloseStream() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseStream(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkStream::~morkStream() // assert CloseStream() executed earlier
+{
+  MORK_ASSERT(mStream_ContentFile==0);
+  MORK_ASSERT(mStream_Buf==0);
+}
+
+/*public non-poly*/
+morkStream::morkStream(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap,
+  nsIMdbFile* ioContentFile, mork_size inBufSize, mork_bool inFrozen)
+: morkFile(ev, inUsage, ioHeap, ioHeap)
+, mStream_At( 0 )
+, mStream_ReadEnd( 0 )
+, mStream_WriteEnd( 0 )
+
+, mStream_ContentFile( 0 )
+
+, mStream_Buf( 0 )
+, mStream_BufSize( inBufSize )
+, mStream_BufPos( 0 )
+, mStream_Dirty( morkBool_kFalse )
+, mStream_HitEof( morkBool_kFalse )
+{
+  if ( ev->Good() )
+  {
+    if ( inBufSize < morkStream_kMinBufSize )
+      mStream_BufSize = inBufSize = morkStream_kMinBufSize;
+    else if ( inBufSize > morkStream_kMaxBufSize )
+      mStream_BufSize = inBufSize = morkStream_kMaxBufSize;
+    
+    if ( ioContentFile && ioHeap )
+    {
+      // if ( ioContentFile->FileFrozen() ) // forced to be readonly?
+      //   inFrozen = morkBool_kTrue; // override the input value
+        
+      nsIMdbFile_SlotStrongFile(ioContentFile, ev, &mStream_ContentFile);
+      if ( ev->Good() )
+      {
+        mork_u1* buf = 0;
+        ioHeap->Alloc(ev->AsMdbEnv(), inBufSize, (void**) &buf);
+        if ( buf )
+        {
+          mStream_At = mStream_Buf = buf;
+          
+          if ( !inFrozen )
+          {
+            // physical buffer end never moves:
+            mStream_WriteEnd = buf + inBufSize;
+          }
+          else
+            mStream_WriteEnd = 0; // no writing is allowed
+          
+          if ( inFrozen )
+          {
+            // logical buffer end starts at Buf with no content:
+            mStream_ReadEnd = buf;
+            this->SetFileFrozen(inFrozen);
+          }
+          else
+            mStream_ReadEnd = 0; // no reading is allowed
+          
+          this->SetFileActive(morkBool_kTrue);
+          this->SetFileIoOpen(morkBool_kTrue);
+        }
+        if ( ev->Good() )
+          mNode_Derived = morkDerived_kStream;
+      }
+    }
+    else ev->NilPointerError();
+  }
+}
+
+/*public non-poly*/ void
+morkStream::CloseStream(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mStream_ContentFile);
+      nsIMdbHeap* heap = mFile_SlotHeap;
+      mork_u1* buf = mStream_Buf;
+      mStream_Buf = 0;
+      
+      if ( heap && buf )
+        heap->Free(ev->AsMdbEnv(), buf);
+
+      this->CloseFile(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+  
+#define morkStream_kSpacesPerIndent 1 /* one space per indent */
+#define morkStream_kMaxIndentDepth 70 /* max indent of 70 space bytes */
+static const char morkStream_kSpaces[] // next line to ease length perception
+= "                                                                        ";
+// 123456789_123456789_123456789_123456789_123456789_123456789_123456789_
+// morkStream_kSpaces above must contain (at least) 70 spaces (ASCII 0x20)
+ 
+mork_size
+morkStream::PutIndent(morkEnv* ev, mork_count inDepth)
+  // PutIndent() puts a linebreak, and then
+  // "indents" by inDepth, and returns the line length after indentation.
+{
+  mork_size outLength = 0;
+  nsIMdbEnv *mev = ev->AsMdbEnv();
+  if ( ev->Good() )
+  {
+    this->PutLineBreak(ev);
+    if ( ev->Good() )
+    {
+      outLength = inDepth;
+      mdb_size bytesWritten;
+      if ( inDepth )
+        this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
+    }
+  }
+  return outLength;
+}
+
+mork_size
+morkStream::PutByteThenIndent(morkEnv* ev, int inByte, mork_count inDepth)
+  // PutByteThenIndent() puts the byte, then a linebreak, and then
+  // "indents" by inDepth, and returns the line length after indentation.
+{
+  mork_size outLength = 0;
+  nsIMdbEnv *mev = ev->AsMdbEnv();
+  
+  if ( inDepth > morkStream_kMaxIndentDepth )
+    inDepth = morkStream_kMaxIndentDepth;
+  
+  this->Putc(ev, inByte);
+  if ( ev->Good() )
+  {
+    this->PutLineBreak(ev);
+    if ( ev->Good() )
+    {
+      outLength = inDepth;
+      mdb_size bytesWritten;
+      if ( inDepth )
+        this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
+    }
+  }
+  return outLength;
+}
+  
+mork_size
+morkStream::PutStringThenIndent(morkEnv* ev,
+  const char* inString, mork_count inDepth)
+// PutStringThenIndent() puts the string, then a linebreak, and then
+// "indents" by inDepth, and returns the line length after indentation.
+{
+  mork_size outLength = 0;
+  mdb_size bytesWritten;
+  nsIMdbEnv *mev = ev->AsMdbEnv();
+  
+  if ( inDepth > morkStream_kMaxIndentDepth )
+    inDepth = morkStream_kMaxIndentDepth;
+  
+  if ( inString )
+  {
+    mork_size length = MORK_STRLEN(inString);
+    if ( length && ev->Good() ) // any bytes to write?
+      this->Write(mev, inString, length, &bytesWritten);
+  }
+  
+  if ( ev->Good() )
+  {
+    this->PutLineBreak(ev);
+    if ( ev->Good() )
+    {
+      outLength = inDepth;
+      if ( inDepth )
+        this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
+    }
+  }
+  return outLength;
+}
+
+mork_size
+morkStream::PutString(morkEnv* ev, const char* inString)
+{
+  nsIMdbEnv *mev = ev->AsMdbEnv();
+  mork_size outSize = 0;
+  mdb_size bytesWritten;
+  if ( inString )
+  {
+    outSize = MORK_STRLEN(inString);
+    if ( outSize && ev->Good() ) // any bytes to write?
+    {
+      this->Write(mev, inString, outSize, &bytesWritten);
+    }
+  }
+  return outSize;
+}
+
+mork_size
+morkStream::PutStringThenNewline(morkEnv* ev, const char* inString)
+  // PutStringThenNewline() returns total number of bytes written.
+{
+  nsIMdbEnv *mev = ev->AsMdbEnv();
+  mork_size outSize = 0;
+  mdb_size bytesWritten;
+  if ( inString )
+  {
+    outSize = MORK_STRLEN(inString);
+    if ( outSize && ev->Good() ) // any bytes to write?
+    {
+      this->Write(mev, inString, outSize, &bytesWritten);
+      if ( ev->Good() )
+        outSize += this->PutLineBreak(ev);
+    }
+  }
+  return outSize;
+}
+
+mork_size
+morkStream::PutByteThenNewline(morkEnv* ev, int inByte)
+  // PutByteThenNewline() returns total number of bytes written.
+{
+  mork_size outSize = 1; // one for the following byte
+  this->Putc(ev, inByte);
+  if ( ev->Good() )
+    outSize += this->PutLineBreak(ev);
+  return outSize;
+}
+
+mork_size
+morkStream::PutLineBreak(morkEnv* ev)
+{
+#if defined(MORK_MAC)
+
+  this->Putc(ev, mork_kCR);
+  return 1;
+  
+#else
+#  if defined(MORK_WIN) || defined(MORK_OS2)
+  
+  this->Putc(ev, mork_kCR);
+  this->Putc(ev, mork_kLF);
+  return 2;
+  
+#  else
+#    ifdef MORK_UNIX
+  
+  this->Putc(ev, mork_kLF);
+  return 1;
+  
+#    endif /* MORK_UNIX */
+#  endif /* MORK_WIN */
+#endif /* MORK_MAC */
+}
+// ````` ````` ````` `````   ````` ````` ````` `````  
+// public: // virtual morkFile methods
+
+
+NS_IMETHODIMP
+morkStream::Steal(nsIMdbEnv* mev, nsIMdbFile* ioThief)
+  // Steal: tell this file to close any associated i/o stream in the file
+  // system, because the file ioThief intends to reopen the file in order
+  // to provide the MDB implementation with more exotic file access than is
+  // offered by the nsIMdbFile alone.  Presumably the thief knows enough
+  // from Path() in order to know which file to reopen.  If Steal() is
+  // successful, this file should probably delegate all future calls to
+  // the nsIMdbFile interface down to the thief files, so that even after
+  // the file has been stolen, it can still be read, written, or forcibly
+  // closed (by a call to CloseMdbObject()).
+{
+  MORK_USED_1(ioThief);
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  ev->StubMethodOnlyError();
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStream::BecomeTrunk(nsIMdbEnv* mev)
+  // If this file is a file version branch created by calling AcquireBud(),
+  // BecomeTrunk() causes this file's content to replace the original
+  // file's content, typically by assuming the original file's identity.
+{
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  ev->StubMethodOnlyError();
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStream::AcquireBud(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, nsIMdbFile **acqBud)
+  // AcquireBud() starts a new "branch" version of the file, empty of content,
+  // so that a new version of the file can be written.  This new file
+  // can later be told to BecomeTrunk() the original file, so the branch
+  // created by budding the file will replace the original file.  Some
+  // file subclasses might initially take the unsafe but expedient
+  // approach of simply truncating this file down to zero length, and
+  // then returning the same morkFile pointer as this, with an extra
+  // reference count increment.  Note that the caller of AcquireBud() is
+  // expected to eventually call CutStrongRef() on the returned file
+  // in order to release the strong reference.  High quality versions
+  // of morkFile subclasses will create entirely new files which later
+  // are renamed to become the old file, so that better transactional
+  // behavior is exhibited by the file, so crashes protect old files.
+  // Note that AcquireBud() is an illegal operation on readonly files.
+{
+  MORK_USED_1(ioHeap);
+  morkFile* outFile = 0;
+  nsIMdbFile* file = mStream_ContentFile;
+  morkEnv *ev = morkEnv::FromMdbEnv(mev);
+  if ( this->IsOpenAndActiveFile() && file )
+  {
+    // figure out how this interacts with buffering and mStream_WriteEnd:
+    ev->StubMethodOnlyError();
+  }
+  else this->NewFileDownError(ev);
+  
+  *acqBud = outFile;
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+mork_pos 
+morkStream::Length(morkEnv* ev) const // eof
+{
+  mork_pos outPos = 0;
+
+  nsIMdbFile* file = mStream_ContentFile;
+  if ( this->IsOpenAndActiveFile() && file )
+  {
+    mork_pos contentEof = 0;
+    file->Eof(ev->AsMdbEnv(), &contentEof);
+    if ( ev->Good() )
+    {
+      if ( mStream_WriteEnd ) // this stream supports writing?
+      {
+        // the local buffer might have buffered content past content eof
+        if ( ev->Good() ) // no error happened during Length() above?
+        {
+          mork_u1* at = mStream_At;
+          mork_u1* buf = mStream_Buf;
+          if ( at >= buf ) // expected cursor order?
+          {
+            mork_pos localContent = mStream_BufPos + (at - buf);
+            if ( localContent > contentEof ) // buffered past eof?
+              contentEof = localContent; // return new logical eof
+
+            outPos = contentEof;
+          }
+          else this->NewBadCursorOrderError(ev);
+        }
+      }
+      else
+        outPos = contentEof; // frozen files get length from content file
+    }
+  }
+  else this->NewFileDownError(ev);
+
+  return outPos;
+}
+
+void morkStream::NewBadCursorSlotsError(morkEnv* ev) const
+{ ev->NewError("bad stream cursor slots"); }
+
+void morkStream::NewNullStreamBufferError(morkEnv* ev) const
+{ ev->NewError("null stream buffer"); }
+
+void morkStream::NewCantReadSinkError(morkEnv* ev) const
+{ ev->NewError("cant read stream sink"); }
+
+void morkStream::NewCantWriteSourceError(morkEnv* ev) const
+{ ev->NewError("cant write stream source"); }
+
+void morkStream::NewPosBeyondEofError(morkEnv* ev) const
+{ ev->NewError("stream pos beyond eof"); }
+
+void morkStream::NewBadCursorOrderError(morkEnv* ev) const
+{ ev->NewError("bad stream cursor order"); }
+
+NS_IMETHODIMP 
+morkStream::Tell(nsIMdbEnv* mdbev, mork_pos *aOutPos) const
+{
+  nsresult rv = NS_OK;
+  morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+
+  NS_ENSURE_ARG_POINTER(aOutPos);
+  
+  nsIMdbFile* file = mStream_ContentFile;
+  if ( this->IsOpenAndActiveFile() && file )
+  {
+    mork_u1* buf = mStream_Buf;
+    mork_u1* at = mStream_At;
+    
+    mork_u1* readEnd = mStream_ReadEnd;   // nonzero only if readonly
+    mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly
+    
+    if ( writeEnd )
+    {
+      if ( buf && at >= buf && at <= writeEnd ) 
+      {
+        *aOutPos = mStream_BufPos + (at - buf);
+      }
+      else this->NewBadCursorOrderError(ev);
+    }
+    else if ( readEnd )
+    {
+      if ( buf && at >= buf && at <= readEnd ) 
+      {
+        *aOutPos = mStream_BufPos + (at - buf);
+      }
+      else this->NewBadCursorOrderError(ev);
+    }
+  }
+  else this->NewFileDownError(ev);
+
+  return rv;
+}
+
+NS_IMETHODIMP 
+morkStream::Read(nsIMdbEnv* mdbev, void* outBuf, mork_size inSize, mork_size *aOutSize)
+{
+  NS_ENSURE_ARG_POINTER(aOutSize);
+  // First we satisfy the request from buffered bytes, if any.  Then
+  // if additional bytes are needed, we satisfy these by direct reads
+  // from the content file without any local buffering (but we still need
+  // to adjust the buffer position to reflect the current i/o point).
+
+  morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+  nsresult rv = NS_OK;
+
+  nsIMdbFile* file = mStream_ContentFile;
+  if ( this->IsOpenAndActiveFile() && file )
+  {
+    mork_u1* end = mStream_ReadEnd; // byte after last buffered byte
+    if ( end ) // file is open for read access?
+    {
+      if ( inSize ) // caller wants any output?
+      {
+        mork_u1* sink = (mork_u1*) outBuf; // where we plan to write bytes
+        if ( sink ) // caller passed good buffer address?
+        {
+          mork_u1* at = mStream_At;
+          mork_u1* buf = mStream_Buf;
+          if ( at >= buf && at <= end ) // expected cursor order?
+          {
+            mork_num remaining = (mork_num) (end - at); // bytes left in buffer
+            
+            mork_num quantum = inSize; // number of bytes to copy
+            if ( quantum > remaining ) // more than buffer content?
+              quantum = remaining; // restrict to buffered bytes
+              
+            if ( quantum ) // any bytes left in the buffer?
+            {
+              MORK_MEMCPY(sink, at, quantum); // from buffer bytes
+              
+              at += quantum; // advance past read bytes
+              mStream_At = at;
+              *aOutSize += quantum;  // this much copied so far
+
+              sink += quantum;   // in case we need to copy more
+              inSize -= quantum; // filled this much of request
+              mStream_HitEof = morkBool_kFalse;
+            }
+            
+            if ( inSize ) // we still need to read more content?
+            {
+              // We need to read more bytes directly from the
+              // content file, without local buffering.  We have
+              // exhausted the local buffer, so we need to show
+              // it is now empty, and adjust the current buf pos.
+              
+              mork_num posDelta = (mork_num) (at - buf); // old buf content
+              mStream_BufPos += posDelta;   // past now empty buf
+              
+              mStream_At = mStream_ReadEnd = buf; // empty buffer
+              
+              // file->Seek(ev, mStream_BufPos); // set file pos
+              // if ( ev->Good() ) // no seek error?
+              // {
+              // }
+              
+              mork_num actual = 0;
+              nsIMdbEnv* menv = ev->AsMdbEnv();
+              file->Get(menv, sink, inSize, mStream_BufPos, &actual);
+              if ( ev->Good() ) // no read error?
+              {
+                if ( actual )
+                {
+                  *aOutSize += actual;
+                  mStream_BufPos += actual;
+                  mStream_HitEof = morkBool_kFalse;
+                }
+                else if ( !*aOutSize )
+                  mStream_HitEof = morkBool_kTrue;
+              }
+            }
+          }
+          else this->NewBadCursorOrderError(ev);
+        }
+        else this->NewNullStreamBufferError(ev);
+      }
+    }
+    else this->NewCantReadSinkError(ev);
+  }
+  else this->NewFileDownError(ev);
+  
+  if ( ev->Bad() )
+    *aOutSize = 0;
+
+  return rv;
+}
+
+NS_IMETHODIMP 
+morkStream::Seek(nsIMdbEnv * mdbev, mork_pos inPos, mork_pos *aOutPos)
+{
+  NS_ENSURE_ARG_POINTER(aOutPos);
+  morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+  *aOutPos = 0;
+  nsresult rv = NS_OK;
+  nsIMdbFile* file = mStream_ContentFile;
+  if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
+  {
+    mork_u1* at = mStream_At;             // current position in buffer
+    mork_u1* buf = mStream_Buf;           // beginning of buffer 
+    mork_u1* readEnd = mStream_ReadEnd;   // nonzero only if readonly
+    mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly
+    
+    if ( writeEnd ) // file is mutable/writeonly?
+    {
+      if ( mStream_Dirty ) // need to commit buffer changes?
+        this->Flush(mdbev);
+
+      if ( ev->Good() ) // no errors during flush or earlier?
+      {
+        if ( at == buf ) // expected post flush cursor value?
+        {
+          if ( mStream_BufPos != inPos ) // need to change pos?
+          {
+            mork_pos eof = 0;
+            nsIMdbEnv* menv = ev->AsMdbEnv();
+            file->Eof(menv, &eof);
+            if ( ev->Good() ) // no errors getting length?
+            {
+              if ( inPos <= eof ) // acceptable new position?
+              {
+                mStream_BufPos = inPos; // new stream position
+                *aOutPos = inPos;
+              }
+              else this->NewPosBeyondEofError(ev);
+            }
+          }
+        }
+        else this->NewBadCursorOrderError(ev);
+      }
+    }
+    else if ( readEnd ) // file is frozen/readonly?
+    {
+      if ( at >= buf && at <= readEnd ) // expected cursor order?
+      {
+        mork_pos eof = 0;
+        nsIMdbEnv* menv = ev->AsMdbEnv();
+        file->Eof(menv, &eof);
+        if ( ev->Good() ) // no errors getting length?
+        {
+          if ( inPos <= eof ) // acceptable new position?
+          {
+            *aOutPos = inPos;
+            mStream_BufPos = inPos; // new stream position
+            mStream_At = mStream_ReadEnd = buf; // empty buffer
+            if ( inPos == eof ) // notice eof reached?
+              mStream_HitEof = morkBool_kTrue;
+          }
+          else this->NewPosBeyondEofError(ev);
+        }
+      }
+      else this->NewBadCursorOrderError(ev);
+    }
+      
+  }
+  else this->NewFileDownError(ev);
+
+  return rv;
+}
+
+NS_IMETHODIMP 
+morkStream::Write(nsIMdbEnv* menv, const void* inBuf, mork_size inSize, mork_size  *aOutSize)
+{
+  mork_num outActual = 0;
+  morkEnv *ev = morkEnv::FromMdbEnv(menv);
+
+  nsIMdbFile* file = mStream_ContentFile;
+  if ( this->IsOpenActiveAndMutableFile() && file )
+  {
+    mork_u1* end = mStream_WriteEnd; // byte after last buffered byte
+    if ( end ) // file is open for write access?
+    {
+      if ( inSize ) // caller provided any input?
+      {
+        const mork_u1* source = (const mork_u1*) inBuf; // from where
+        if ( source ) // caller passed good buffer address?
+        {
+          mork_u1* at = mStream_At;
+          mork_u1* buf = mStream_Buf;
+          if ( at >= buf && at <= end ) // expected cursor order?
+          {
+            mork_num space = (mork_num) (end - at); // space left in buffer
+            
+            mork_num quantum = inSize; // number of bytes to write
+            if ( quantum > space ) // more than buffer size?
+              quantum = space; // restrict to avail space
+              
+            if ( quantum ) // any space left in the buffer?
+            {
+              mStream_Dirty = morkBool_kTrue; // to ensure later flush
+              MORK_MEMCPY(at, source, quantum); // into buffer
+              
+              mStream_At += quantum; // advance past written bytes
+              outActual += quantum;  // this much written so far
+
+              source += quantum; // in case we need to write more
+              inSize -= quantum; // filled this much of request
+            }
+            
+            if ( inSize ) // we still need to write more content?
+            {
+              // We need to write more bytes directly to the
+              // content file, without local buffering.  We have
+              // exhausted the local buffer, so we need to flush
+              // it and empty it, and adjust the current buf pos.
+              // After flushing, if the rest of the write fits
+              // inside the buffer, we will put bytes into the
+              // buffer rather than write them to content file.
+              
+              if ( mStream_Dirty )
+                this->Flush(menv); // will update mStream_BufPos
+
+              at = mStream_At;
+              if ( at < buf || at > end ) // bad cursor?
+                this->NewBadCursorOrderError(ev);
+                
+              if ( ev->Good() ) // no errors?
+              {
+                space = (mork_num) (end - at); // space left in buffer
+                if ( space > inSize ) // write to buffer?
+                {
+                  mStream_Dirty = morkBool_kTrue; // ensure flush
+                  MORK_MEMCPY(at, source, inSize); // copy
+                  
+                  mStream_At += inSize; // past written bytes
+                  outActual += inSize;  // this much written
+                }
+                else // directly to content file instead
+                {
+                  // file->Seek(ev, mStream_BufPos); // set pos
+                  // if ( ev->Good() ) // no seek error?
+                  // {
+                  // }
+
+                  mork_num actual = 0;
+                  file->Put(menv, source, inSize, mStream_BufPos, &actual);
+                  if ( ev->Good() ) // no write error?
+                  {
+                    outActual += actual;
+                    mStream_BufPos += actual;
+                  }
+                }
+              }
+            }
+          }
+          else this->NewBadCursorOrderError(ev);
+        }
+        else this->NewNullStreamBufferError(ev);
+      }
+    }
+    else this->NewCantWriteSourceError(ev);
+  }
+  else this->NewFileDownError(ev);
+  
+  if ( ev->Bad() )
+    outActual = 0;
+
+  *aOutSize = outActual;
+  return ev->AsErr();
+}
+
+NS_IMETHODIMP     
+morkStream::Flush(nsIMdbEnv* ev)
+{
+  morkEnv *mev = morkEnv::FromMdbEnv(ev);
+  nsresult rv = NS_ERROR_FAILURE;
+  nsIMdbFile* file = mStream_ContentFile;
+  if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
+  {
+    if ( mStream_Dirty )
+      this->spill_buf(mev);
+
+    rv = file->Flush(ev);
+  }
+  else this->NewFileDownError(mev);
+  return rv;
+}
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+// protected: // protected non-poly morkStream methods (for char io)
+
+int
+morkStream::fill_getc(morkEnv* ev)
+{
+  int c = EOF;
+  
+  nsIMdbFile* file = mStream_ContentFile;
+  if ( this->IsOpenAndActiveFile() && file )
+  {
+    mork_u1* buf = mStream_Buf;
+    mork_u1* end = mStream_ReadEnd; // beyond buf after earlier read
+    if ( end > buf ) // any earlier read bytes buffered?
+    {
+      mStream_BufPos += ( end - buf ); // advance past old read
+    }
+      
+    if ( ev->Good() ) // no errors yet?
+    {
+      // file->Seek(ev, mStream_BufPos); // set file pos
+      // if ( ev->Good() ) // no seek error?
+      // {
+      // }
+
+      nsIMdbEnv* menv = ev->AsMdbEnv();
+      mork_num actual = 0;
+      file->Get(menv, buf, mStream_BufSize, mStream_BufPos, &actual);
+      if ( ev->Good() ) // no read errors?
+      {
+        if ( actual > mStream_BufSize ) // more than asked for??
+          actual = mStream_BufSize;
+        
+        mStream_At = buf;
+        mStream_ReadEnd = buf + actual;
+        if ( actual ) // any bytes actually read?
+        {
+          c = *mStream_At++; // return first byte from buffer
+          mStream_HitEof = morkBool_kFalse;
+        }
+        else
+          mStream_HitEof = morkBool_kTrue;
+      }
+    }
+  }
+  else this->NewFileDownError(ev);
+  
+  return c;
+}
+
+void
+morkStream::spill_putc(morkEnv* ev, int c)
+{
+  this->spill_buf(ev);
+  if ( ev->Good() && mStream_At < mStream_WriteEnd )
+    this->Putc(ev, c);
+}
+
+void
+morkStream::spill_buf(morkEnv* ev) // spill/flush from buffer to file
+{
+  nsIMdbFile* file = mStream_ContentFile;
+  if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
+  {
+    mork_u1* buf = mStream_Buf;
+    if ( mStream_Dirty )
+    {
+      mork_u1* at = mStream_At;
+      if ( at >= buf && at <= mStream_WriteEnd ) // order?
+      {
+        mork_num count = (mork_num) (at - buf); // bytes buffered
+        if ( count ) // anything to write to the string?
+        {
+          if ( count > mStream_BufSize ) // no more than max?
+          {
+            count = mStream_BufSize;
+            mStream_WriteEnd = buf + mStream_BufSize;
+            this->NewBadCursorSlotsError(ev);
+          }
+          if ( ev->Good() )
+          {
+            // file->Seek(ev, mStream_BufPos);
+            // if ( ev->Good() )
+            // {
+            // }
+            nsIMdbEnv* menv = ev->AsMdbEnv();
+            mork_num actual = 0;
+            
+            file->Put(menv, buf, count, mStream_BufPos, &actual);
+            if ( ev->Good() )
+            {
+              mStream_BufPos += actual; // past bytes written
+              mStream_At = buf; // reset buffer cursor
+              mStream_Dirty = morkBool_kFalse;
+            }
+          }
+        }
+      }
+      else this->NewBadCursorOrderError(ev);
+    }
+    else
+    {
+#ifdef MORK_DEBUG
+      ev->NewWarning("stream:spill:not:dirty");
+#endif /*MORK_DEBUG*/
+    }
+  }
+  else this->NewFileDownError(ev);
+
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkStream.h
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKSTREAM_
+#define _MORKSTREAM_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKFILE_
+#include "morkFile.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*=============================================================================
+ * morkStream: buffered file i/o
+ */
+
+/*| morkStream exists to define an morkFile subclass that provides buffered
+**| i/o for an underlying content file.  Naturally this arrangement only makes
+**| sense when the underlying content file is itself not efficiently buffered
+**| (especially for character by character i/o).
+**|
+**|| morkStream is intended for either reading use or writing use, but not
+**| both simultaneously or interleaved.  Pick one when the stream is created
+**| and don't change your mind.  This restriction is intended to avoid obscure
+**| and complex bugs that might arise from interleaved reads and writes -- so
+**| just don't do it.  A stream is either a sink or a source, but not both.
+**|
+**|| (When the underlying content file is intended to support both reading and
+**| writing, a developer might use two instances of morkStream where one is for
+**| reading and the other is for writing.  In this case, a developer must take
+**| care to keep the two streams in sync because each will maintain a separate
+**| buffer representing a cache consistency problem for the other.  A simple
+**| approach is to invalidate the buffer of one when one uses the other, with
+**| the assumption that closely mixed reading and writing is not expected, so
+**| that little cost is associated with changing read/write streaming modes.)
+**|
+**|| Exactly one of mStream_ReadEnd or mStream_WriteEnd must be a null pointer,
+**| and this will cause the right thing to occur when inlines use them, because
+**| mStream_At < mStream_WriteEnd (for example) will always be false and the
+**| else branch of the statement calls a function that raises an appropriate
+**| error to complain about either reading a sink or writing a source.
+**|
+**|| morkStream is a direct clone of ab_Stream from Communicator 4.5's
+**| address book code, which in turn was based on the stream class in the
+**| public domain Mithril programming language.
+|*/
+
+#define morkStream_kPrintBufSize /*i*/ 512 /* buffer size used by printf() */ 
+
+#define morkStream_kMinBufSize /*i*/ 512 /* buffer no fewer bytes */ 
+#define morkStream_kMaxBufSize /*i*/ (32 * 1024) /* buffer no more bytes */ 
+
+#define morkDerived_kStream     /*i*/ 0x7A74 /* ascii 'zt' */
+
+class morkStream /*d*/ : public morkFile { /* from Mithril's AgStream class */
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+protected: // protected morkStream members
+  mork_u1*    mStream_At;       // pointer into mStream_Buf
+  mork_u1*    mStream_ReadEnd;  // null or one byte past last readable byte
+  mork_u1*    mStream_WriteEnd; // null or mStream_Buf + mStream_BufSize
+
+  nsIMdbFile* mStream_ContentFile;  // where content is read and written
+
+  mork_u1*    mStream_Buf;      // dynamically allocated memory to buffer io
+  mork_size   mStream_BufSize;  // requested buf size (fixed by min and max)
+  mork_pos    mStream_BufPos;   // logical position of byte at mStream_Buf
+  mork_bool   mStream_Dirty;    // does the buffer need to be flushed?
+  mork_bool   mStream_HitEof;   // has eof been reached? (only frozen streams)
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseStream() only if open
+  virtual ~morkStream(); // assert that CloseStream() executed earlier
+  
+public: // morkStream construction & destruction
+  morkStream(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+      nsIMdbFile* ioContentFile, mork_size inBufSize, mork_bool inFrozen);
+  void CloseStream(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkStream(const morkStream& other);
+  morkStream& operator=(const morkStream& other);
+
+public: // dynamic type identification
+  mork_bool IsStream() const
+  { return IsNode() && mNode_Derived == morkDerived_kStream; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  void NonStreamTypeError(morkEnv* ev);
+
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // virtual morkFile methods
+
+  NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief);
+  // Steal: tell this file to close any associated i/o stream in the file
+  // system, because the file ioThief intends to reopen the file in order
+  // to provide the MDB implementation with more exotic file access than is
+  // offered by the nsIMdbFile alone.  Presumably the thief knows enough
+  // from Path() in order to know which file to reopen.  If Steal() is
+  // successful, this file should probably delegate all future calls to
+  // the nsIMdbFile interface down to the thief files, so that even after
+  // the file has been stolen, it can still be read, written, or forcibly
+  // closed (by a call to CloseMdbObject()).
+
+  NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev);
+  // If this file is a file version branch created by calling AcquireBud(),
+  // BecomeTrunk() causes this file's content to replace the original
+  // file's content, typically by assuming the original file's identity.
+
+  NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbFile** acqBud);
+  // AcquireBud() starts a new "branch" version of the file, empty of content,
+  // so that a new version of the file can be written.  This new file
+  // can later be told to BecomeTrunk() the original file, so the branch
+  // created by budding the file will replace the original file.  Some
+  // file subclasses might initially take the unsafe but expedient
+  // approach of simply truncating this file down to zero length, and
+  // then returning the same morkFile pointer as this, with an extra
+  // reference count increment.  Note that the caller of AcquireBud() is
+  // expected to eventually call CutStrongRef() on the returned file
+  // in order to release the strong reference.  High quality versions
+  // of morkFile subclasses will create entirely new files which later
+  // are renamed to become the old file, so that better transactional
+  // behavior is exhibited by the file, so crashes protect old files.
+  // Note that AcquireBud() is an illegal operation on readonly files.
+  
+  virtual mork_pos Length(morkEnv* ev) const; // eof
+  NS_IMETHOD  Tell(nsIMdbEnv* ev, mork_pos *aOutPos  ) const;
+  NS_IMETHOD  Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize, mork_size *aOutCount);
+  NS_IMETHOD  Seek(nsIMdbEnv* ev, mork_pos inPos, mork_pos *aOutPos);
+  NS_IMETHOD  Write(nsIMdbEnv* ev, const void* inBuf, mork_size inSize, mork_size *aOutCount);
+  NS_IMETHOD  Flush(nsIMdbEnv* ev);
+    
+// ````` ````` ````` `````   ````` ````` ````` `````  
+protected: // protected non-poly morkStream methods (for char io)
+
+  int     fill_getc(morkEnv* ev);
+  void    spill_putc(morkEnv* ev, int c);
+  void    spill_buf(morkEnv* ev); // spill/flush from buffer to file
+      
+// ````` ````` ````` `````   ````` ````` ````` `````  
+public: // public non-poly morkStream methods
+    
+  void NewBadCursorSlotsError(morkEnv* ev) const;
+  void NewBadCursorOrderError(morkEnv* ev) const;
+  void NewNullStreamBufferError(morkEnv* ev) const;
+  void NewCantReadSinkError(morkEnv* ev) const;
+  void NewCantWriteSourceError(morkEnv* ev) const;
+  void NewPosBeyondEofError(morkEnv* ev) const;
+      
+  nsIMdbFile* GetStreamContentFile() const { return mStream_ContentFile; }
+  mork_size   GetStreamBufferSize() const { return mStream_BufSize; }
+  
+  mork_size  PutIndent(morkEnv* ev, mork_count inDepth);
+  // PutIndent() puts a linebreak, and then
+  // "indents" by inDepth, and returns the line length after indentation.
+  
+  mork_size  PutByteThenIndent(morkEnv* ev, int inByte, mork_count inDepth);
+  // PutByteThenIndent() puts the byte, then a linebreak, and then
+  // "indents" by inDepth, and returns the line length after indentation.
+  
+  mork_size  PutStringThenIndent(morkEnv* ev,
+    const char* inString, mork_count inDepth);
+  // PutStringThenIndent() puts the string, then a linebreak, and then
+  // "indents" by inDepth, and returns the line length after indentation.
+  
+  mork_size  PutString(morkEnv* ev, const char* inString);
+  // PutString() returns the length of the string written.
+  
+  mork_size  PutStringThenNewline(morkEnv* ev, const char* inString);
+  // PutStringThenNewline() returns total number of bytes written.
+
+  mork_size  PutByteThenNewline(morkEnv* ev, int inByte);
+  // PutByteThenNewline() returns total number of bytes written.
+
+  // ````` ````` stdio type methods ````` ````` 
+  void    Ungetc(int c) /*i*/
+  { if ( mStream_At > mStream_Buf && c > 0 ) *--mStream_At = (mork_u1) c; }
+  
+  // Note Getc() returns EOF consistently after any fill_getc() error occurs.
+  int     Getc(morkEnv* ev) /*i*/
+  { return ( mStream_At < mStream_ReadEnd )? *mStream_At++ : fill_getc(ev); }
+  
+  void    Putc(morkEnv* ev, int c) /*i*/
+  { 
+    mStream_Dirty = morkBool_kTrue;
+    if ( mStream_At < mStream_WriteEnd )
+      *mStream_At++ = (mork_u1) c;
+    else
+      spill_putc(ev, c);
+  }
+
+  mork_size PutLineBreak(morkEnv* ev);
+  
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakStream(morkStream* me,
+    morkEnv* ev, morkStream** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongStream(morkStream* me,
+    morkEnv* ev, morkStream** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSTREAM_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkTable.cpp
@@ -0,0 +1,1631 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkArray.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKTABLEROWCURSOR_
+#include "morkTableRowCursor.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkTable::CloseMorkNode(morkEnv* ev) /*i*/ // CloseTable() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    morkObject::CloseMorkNode(ev); // give base class a chance.
+    this->MarkClosing();
+    this->CloseTable(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkTable::~morkTable() /*i*/ // assert CloseTable() executed earlier
+{
+  CloseMorkNode(mMorkEnv);
+  MORK_ASSERT(this->IsShutNode());
+  MORK_ASSERT(mTable_Store==0);
+  MORK_ASSERT(mTable_RowSpace==0);
+}
+
+/*public non-poly*/
+morkTable::morkTable(morkEnv* ev, /*i*/
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap, 
+  morkStore* ioStore, nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace,
+  const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+  mork_tid inTid, mork_kind inKind, mork_bool inMustBeUnique)
+: morkObject(ev, inUsage, ioHeap, (mork_color) inTid, (morkHandle*) 0)
+, mTable_Store( 0 )
+, mTable_RowSpace( 0 )
+, mTable_MetaRow( 0 )
+
+, mTable_RowMap( 0 )
+// , mTable_RowMap(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap,
+//   morkTable_kStartRowMapSlotCount)
+, mTable_RowArray(ev, morkUsage::kMember, (nsIMdbHeap*) 0,
+  morkTable_kStartRowArraySize, ioSlotHeap)
+  
+, mTable_ChangeList()
+, mTable_ChangesCount( 0 )
+, mTable_ChangesMax( 3 ) // any very small number greater than zero
+
+, mTable_Kind( inKind )
+
+, mTable_Flags( 0 )
+, mTable_Priority( morkPriority_kLo ) // NOT high priority
+, mTable_GcUses( 0 )
+, mTable_Pad( 0 )
+{
+  this->mLink_Next = 0;
+  this->mLink_Prev = 0;
+  
+  if ( ev->Good() )
+  {
+    if ( ioStore && ioSlotHeap && ioRowSpace )
+    {
+      if ( inKind )
+      {
+        if ( inMustBeUnique )
+          this->SetTableUnique();
+        mTable_Store = ioStore;
+        mTable_RowSpace = ioRowSpace;
+        if ( inOptionalMetaRowOid )
+          mTable_MetaRowOid = *inOptionalMetaRowOid;
+        else
+        {
+          mTable_MetaRowOid.mOid_Scope = 0;
+          mTable_MetaRowOid.mOid_Id = morkRow_kMinusOneRid;
+        }
+        if ( ev->Good() )
+        {
+          if ( this->MaybeDirtySpaceStoreAndTable() )
+            this->SetTableRewrite(); // everything is dirty
+            
+          mNode_Derived = morkDerived_kTable;
+        }
+        this->MaybeDirtySpaceStoreAndTable(); // new table might dirty store
+      }
+      else
+        ioRowSpace->ZeroKindError(ev);
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkTable, morkObject, nsIMdbTable)
+
+/*public non-poly*/ void
+morkTable::CloseTable(morkEnv* ev) /*i*/ // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      morkRowMap::SlotStrongRowMap((morkRowMap*) 0, ev, &mTable_RowMap);
+      // mTable_RowMap.CloseMorkNode(ev);
+      mTable_RowArray.CloseMorkNode(ev);
+      mTable_Store = 0;
+      mTable_RowSpace = 0;
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+// { ===== begin nsIMdbCollection methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkTable::GetSeed(nsIMdbEnv* mev,
+  mdb_seed* outSeed)    // member change count
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    *outSeed = mTable_RowArray.mArray_Seed;
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkTable::GetCount(nsIMdbEnv* mev,
+  mdb_count* outCount) // member count
+{
+  NS_ENSURE_ARG_POINTER(outCount);
+  *outCount = mTable_RowArray.mArray_Fill;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::GetPort(nsIMdbEnv* mev,
+  nsIMdbPort** acqPort) // collection container
+{
+  (void) morkEnv::FromMdbEnv(mev);
+  NS_ENSURE_ARG_POINTER(acqPort);    
+  *acqPort = mTable_Store;
+  return NS_OK;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin cursor methods -----
+NS_IMETHODIMP
+morkTable::GetCursor( // make a cursor starting iter at inMemberPos
+  nsIMdbEnv* mev, // context
+  mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+  nsIMdbCursor** acqCursor) // acquire new cursor instance
+{
+  return this->GetTableRowCursor(mev, inMemberPos,
+    (nsIMdbTableRowCursor**) acqCursor);
+}
+// } ----- end cursor methods -----
+
+// { ----- begin ID methods -----
+NS_IMETHODIMP
+morkTable::GetOid(nsIMdbEnv* mev,
+  mdbOid* outOid) // read object identity
+{
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  GetTableOid(ev, outOid);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::BecomeContent(nsIMdbEnv* mev,
+  const mdbOid* inOid) // exchange content
+{
+  NS_ASSERTION(PR_FALSE, "not implemented"); 
+  return NS_ERROR_NOT_IMPLEMENTED;
+  // remember table->MaybeDirtySpaceStoreAndTable();
+}
+
+// } ----- end ID methods -----
+
+// { ----- begin activity dropping methods -----
+NS_IMETHODIMP
+morkTable::DropActivity( // tell collection usage no longer expected
+  nsIMdbEnv* mev)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented"); 
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+
+// { ===== begin nsIMdbTable methods =====
+
+// { ----- begin attribute methods -----
+
+NS_IMETHODIMP
+morkTable::SetTablePriority(nsIMdbEnv* mev, mdb_priority inPrio)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( inPrio > morkPriority_kMax )
+      inPrio = morkPriority_kMax;
+      
+    mTable_Priority = inPrio;
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::GetTablePriority(nsIMdbEnv* mev, mdb_priority* outPrio)
+{
+  mdb_err outErr = 0;
+  mork_priority prio = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    prio = mTable_Priority;
+    if ( prio > morkPriority_kMax )
+    {
+      prio = morkPriority_kMax;
+      mTable_Priority = prio;
+    }
+    outErr = ev->AsErr();
+  }
+  if ( outPrio )
+    *outPrio = prio;
+  return outErr;
+}
+
+
+NS_IMETHODIMP
+morkTable:: GetTableBeVerbose(nsIMdbEnv* mev, mdb_bool* outBeVerbose)
+{
+  NS_ENSURE_ARG_POINTER(outBeVerbose);
+  *outBeVerbose = IsTableVerbose();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::SetTableBeVerbose(nsIMdbEnv* mev, mdb_bool inBeVerbose)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( inBeVerbose )
+      SetTableVerbose();
+    else
+      ClearTableVerbose();
+   
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::GetTableIsUnique(nsIMdbEnv* mev, mdb_bool* outIsUnique)
+{
+  NS_ENSURE_ARG_POINTER(outIsUnique);
+  *outIsUnique = IsTableUnique();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind)
+{
+  NS_ENSURE_ARG_POINTER(outTableKind);
+  *outTableKind = mTable_Kind;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope)
+{
+  mdb_err outErr = 0;
+  mdb_scope rowScope = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( mTable_RowSpace )
+      rowScope = mTable_RowSpace->SpaceScope();
+    else
+      NilRowSpaceError(ev);
+
+    outErr = ev->AsErr();
+  }
+  if ( outRowScope )
+    *outRowScope = rowScope;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::GetMetaRow( nsIMdbEnv* mev,
+  const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+  mdbOid* outOid, // output meta row oid, can be nil to suppress output
+  nsIMdbRow** acqRow) // acquire table's unique singleton meta row
+  // The purpose of a meta row is to support the persistent recording of
+  // meta info about a table as cells put into the distinguished meta row.
+  // Each table has exactly one meta row, which is not considered a member
+  // of the collection of rows inside the table.  The only way to tell
+  // whether a row is a meta row is by the fact that it is returned by this
+  // GetMetaRow() method from some table. Otherwise nothing distinguishes
+  // a meta row from any other row.  A meta row can be used anyplace that
+  // any other row can be used, and can even be put into other tables (or
+  // the same table) as a table member, if this is useful for some reason.
+  // The first attempt to access a table's meta row using GetMetaRow() will
+  // cause the meta row to be created if it did not already exist.  When the
+  // meta row is created, it will have the row oid that was previously
+  // requested for this table's meta row; or if no oid was ever explicitly
+  // specified for this meta row, then a unique oid will be generated in
+  // the row scope named "metaScope" (so obviously MDB clients should not
+  // manually allocate any row IDs from that special meta scope namespace).
+  // The meta row oid can be specified either when the table is created, or
+  // else the first time that GetMetaRow() is called, by passing a non-nil
+  // pointer to an oid for parameter inOptionalMetaRowOid.  The meta row's
+  // actual oid is returned in outOid (if this is a non-nil pointer), and
+  // it will be different from inOptionalMetaRowOid when the meta row was
+  // already given a different oid earlier.
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRow* row = GetMetaRow(ev, inOptionalMetaRowOid);
+    if ( row && ev->Good() )
+    {
+      if ( outOid )
+        *outOid = row->mRow_Oid;
+        
+      outRow = row->AcquireRowHandle(ev, mTable_Store);
+    }
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+    
+  if ( ev->Bad() && outOid )
+  {
+    outOid->mOid_Scope = 0;
+    outOid->mOid_Id = morkRow_kMinusOneRid;
+  }
+  return outErr;
+}
+
+// } ----- end attribute methods -----
+
+// { ----- begin cursor methods -----
+NS_IMETHODIMP
+morkTable::GetTableRowCursor( // make a cursor, starting iteration at inRowPos
+  nsIMdbEnv* mev, // context
+  mdb_pos inRowPos, // zero-based ordinal position of row in table
+  nsIMdbTableRowCursor** acqCursor) // acquire new cursor instance
+{
+  mdb_err outErr = 0;
+  nsIMdbTableRowCursor* outCursor = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkTableRowCursor* cursor = NewTableRowCursor(ev, inRowPos);
+    if ( cursor )
+    {
+      if ( ev->Good() )
+      {
+        // cursor->mCursor_Seed = (mork_seed) inRowPos;
+        outCursor = cursor;
+        outCursor->AddRef();
+      }
+    }
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqCursor )
+    *acqCursor = outCursor;
+  return outErr;
+}
+// } ----- end row position methods -----
+
+// { ----- begin row position methods -----
+NS_IMETHODIMP
+morkTable::PosToOid( // get row member for a table position
+  nsIMdbEnv* mev, // context
+  mdb_pos inRowPos, // zero-based ordinal position of row in table
+  mdbOid* outOid) // row oid at the specified position
+{
+  mdb_err outErr = 0;
+  mdbOid roid;
+  roid.mOid_Scope = 0;
+  roid.mOid_Id = (mork_id) -1;
+  
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRow* row = SafeRowAt(ev, inRowPos);
+    if ( row )
+      roid = row->mRow_Oid;
+    
+    outErr = ev->AsErr();
+  }
+  if ( outOid )
+    *outOid = roid;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::OidToPos( // test for the table position of a row member
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid, // row to find in table
+  mdb_pos* outPos) // zero-based ordinal position of row in table
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    mork_pos pos = ArrayHasOid(ev, inOid);
+    if ( outPos )
+      *outPos = pos;
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::PosToRow( // get row member for a table position
+  nsIMdbEnv* mev, // context
+  mdb_pos inRowPos, // zero-based ordinal position of row in table
+  nsIMdbRow** acqRow) // acquire row at table position inRowPos
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRow* row = SafeRowAt(ev, inRowPos);
+    if ( row && mTable_Store )
+      outRow = row->AcquireRowHandle(ev, mTable_Store);
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::RowToPos( // test for the table position of a row member
+  nsIMdbEnv* mev, // context
+  nsIMdbRow* ioRow, // row to find in table
+  mdb_pos* outPos) // zero-based ordinal position of row in table
+{
+  mdb_err outErr = 0;
+  mork_pos pos = -1;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRowObject* row = (morkRowObject*) ioRow;
+    pos = ArrayHasOid(ev, &row->mRowObject_Row->mRow_Oid);
+    outErr = ev->AsErr();
+  }
+  if ( outPos )
+    *outPos = pos;
+  return outErr;
+}
+  
+// Note that HasRow() performs the inverse oid->pos mapping
+// } ----- end row position methods -----
+
+// { ----- begin oid set methods -----
+NS_IMETHODIMP
+morkTable::AddOid( // make sure the row with inOid is a table member 
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid) // row to ensure membership in table
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::HasOid( // test for the table position of a row member
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid, // row to find in table
+  mdb_bool* outHasOid) // whether inOid is a member row
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( outHasOid )
+      *outHasOid = MapHasOid(ev, inOid);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::CutOid( // make sure the row with inOid is not a member 
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid) // row to remove from table
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( inOid && mTable_Store )
+    {
+      morkRow* row = mTable_Store->GetRow(ev, inOid);
+      if ( row )
+        CutRow(ev, row);
+    }
+    else
+      ev->NilPointerError();
+      
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ----- end oid set methods -----
+
+// { ----- begin row set methods -----
+NS_IMETHODIMP
+morkTable::NewRow( // create a new row instance in table
+  nsIMdbEnv* mev, // context
+  mdbOid* ioOid, // please use zero (unbound) rowId for db-assigned IDs
+  nsIMdbRow** acqRow) // create new row
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( ioOid && mTable_Store )
+    {
+      morkRow* row = 0;
+      if ( ioOid->mOid_Id == morkRow_kMinusOneRid )
+        row = mTable_Store->NewRow(ev, ioOid->mOid_Scope);
+      else
+        row = mTable_Store->NewRowWithOid(ev, ioOid);
+        
+      if ( row && AddRow(ev, row) )
+        outRow = row->AcquireRowHandle(ev, mTable_Store);
+    }
+    else
+      ev->NilPointerError();
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::AddRow( // make sure the row with inOid is a table member 
+  nsIMdbEnv* mev, // context
+  nsIMdbRow* ioRow) // row to ensure membership in table
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRowObject *rowObj = (morkRowObject *) ioRow;
+    morkRow* row = rowObj->mRowObject_Row;
+    AddRow(ev, row);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::HasRow( // test for the table position of a row member
+  nsIMdbEnv* mev, // context
+  nsIMdbRow* ioRow, // row to find in table
+  mdb_bool* outBool) // zero-based ordinal position of row in table
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRowObject *rowObj = (morkRowObject *) ioRow;
+    morkRow* row = rowObj->mRowObject_Row;
+    if ( outBool )
+      *outBool = MapHasOid(ev, &row->mRow_Oid);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+
+NS_IMETHODIMP
+morkTable::CutRow( // make sure the row with inOid is not a member 
+  nsIMdbEnv* mev, // context
+  nsIMdbRow* ioRow) // row to remove from table
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRowObject *rowObj = (morkRowObject *) ioRow;
+    morkRow* row = rowObj->mRowObject_Row;
+    CutRow(ev, row);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::CutAllRows( // remove all rows from the table 
+  nsIMdbEnv* mev) // context
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    CutAllRows(ev);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ----- end row set methods -----
+
+// { ----- begin searching methods -----
+NS_IMETHODIMP
+morkTable::FindRowMatches( // search variable number of sorted cols
+  nsIMdbEnv* mev, // context
+  const mdbYarn* inPrefix, // content to find as prefix in row's column cell
+  nsIMdbTableRowCursor** acqCursor) // set of matching rows
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+  
+NS_IMETHODIMP
+morkTable::GetSearchColumns( // query columns used by FindRowMatches()
+  nsIMdbEnv* mev, // context
+  mdb_count* outCount, // context
+  mdbColumnSet* outColSet) // caller supplied space to put columns
+  // GetSearchColumns() returns the columns actually searched when the
+  // FindRowMatches() method is called.  No more than mColumnSet_Count
+  // slots of mColumnSet_Columns will be written, since mColumnSet_Count
+  // indicates how many slots are present in the column array.  The
+  // actual number of search column used by the table is returned in
+  // the outCount parameter; if this number exceeds mColumnSet_Count,
+  // then a caller needs a bigger array to read the entire column set.
+  // The minimum of mColumnSet_Count and outCount is the number slots
+  // in mColumnSet_Columns that were actually written by this method.
+  //
+  // Callers are expected to change this set of columns by calls to
+  // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end searching methods -----
+
+// { ----- begin hinting methods -----
+NS_IMETHODIMP
+morkTable::SearchColumnsHint( // advise re future expected search cols  
+  nsIMdbEnv* mev, // context
+  const mdbColumnSet* inColumnSet) // columns likely to be searched
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+  
+NS_IMETHODIMP
+morkTable::SortColumnsHint( // advise re future expected sort columns  
+  nsIMdbEnv* mev, // context
+  const mdbColumnSet* inColumnSet) // columns for likely sort requests
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::StartBatchChangeHint( // advise before many adds and cuts  
+  nsIMdbEnv* mev, // context
+  const void* inLabel) // intend unique address to match end call
+  // If batch starts nest by virtue of nesting calls in the stack, then
+  // the address of a local variable makes a good batch start label that
+  // can be used at batch end time, and such addresses remain unique.
+{
+  // we don't do anything here.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::EndBatchChangeHint( // advise before many adds and cuts  
+  nsIMdbEnv* mev, // context
+  const void* inLabel) // label matching start label
+  // Suppose a table is maintaining one or many sort orders for a table,
+  // so that every row added to the table must be inserted in each sort,
+  // and every row cut must be removed from each sort.  If a db client
+  // intends to make many such changes before needing any information
+  // about the order or positions of rows inside a table, then a client
+  // might tell the table to start batch changes in order to disable
+  // sorting of rows for the interim.  Presumably a table will then do
+  // a full sort of all rows at need when the batch changes end, or when
+  // a surprise request occurs for row position during batch changes.
+{
+  // we don't do anything here.
+  return NS_OK;
+}
+// } ----- end hinting methods -----
+
+// { ----- begin sorting methods -----
+// sorting: note all rows are assumed sorted by row ID as a secondary
+// sort following the primary column sort, when table rows are sorted.
+
+NS_IMETHODIMP
+morkTable::CanSortColumn( // query which column is currently used for sorting
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // column to query sorting potential
+  mdb_bool* outCanSort) // whether the column can be sorted
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::GetSorting( // view same table in particular sorting
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // requested new column for sorting table
+  nsIMdbSorting** acqSorting) // acquire sorting for column
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::SetSearchSorting( // use this sorting in FindRowMatches()
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
+  nsIMdbSorting* ioSorting) // requested sorting for some column
+  // SetSearchSorting() attempts to inform the table that ioSorting
+  // should be used during calls to FindRowMatches() for searching
+  // the column which is actually sorted by ioSorting.  This method
+  // is most useful in conjunction with nsIMdbSorting::SetCompare(),
+  // because otherwise a caller would not be able to override the
+  // comparison ordering method used during searchs.  Note that some
+  // database implementations might be unable to use an arbitrarily
+  // specified sort order, either due to schema or runtime interface
+  // constraints, in which case ioSorting might not actually be used.
+  // Presumably ioSorting is an instance that was returned from some
+  // earlier call to nsIMdbTable::GetSorting().  A caller can also
+  // use nsIMdbTable::SearchColumnsHint() to specify desired change
+  // in which columns are sorted and searched by FindRowMatches().
+  //
+  // A caller can pass a nil pointer for ioSorting to request that
+  // column inColumn no longer be used at all by FindRowMatches().
+  // But when ioSorting is non-nil, then inColumn should match the
+  // column actually sorted by ioSorting; when these do not agree,
+  // implementations are instructed to give precedence to the column
+  // specified by ioSorting (so this means callers might just pass
+  // zero for inColumn when ioSorting is also provided, since then
+  // inColumn is both redundant and ignored).
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// } ----- end sorting methods -----
+
+// { ----- begin moving methods -----
+// moving a row does nothing unless a table is currently unsorted
+
+NS_IMETHODIMP
+morkTable::MoveOid( // change position of row in unsorted table
+  nsIMdbEnv* mev, // context
+  const mdbOid* inOid,  // row oid to find in table
+  mdb_pos inHintFromPos, // suggested hint regarding start position
+  mdb_pos inToPos,       // desired new position for row inOid
+  mdb_pos* outActualPos) // actual new position of row in table
+{
+  mdb_err outErr = 0;
+  mdb_pos actualPos = -1; // meaning it was never found in table
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( inOid && mTable_Store )
+    {
+      morkRow* row = mTable_Store->GetRow(ev, inOid);
+      if ( row )
+        actualPos = MoveRow(ev, row, inHintFromPos, inToPos);
+    }
+    else
+      ev->NilPointerError();
+
+    outErr = ev->AsErr();
+  }
+  if ( outActualPos )
+    *outActualPos = actualPos;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::MoveRow( // change position of row in unsorted table
+  nsIMdbEnv* mev, // context
+  nsIMdbRow* ioRow,  // row oid to find in table
+  mdb_pos inHintFromPos, // suggested hint regarding start position
+  mdb_pos inToPos,       // desired new position for row ioRow
+  mdb_pos* outActualPos) // actual new position of row in table
+{
+  mdb_pos actualPos = -1; // meaning it was never found in table
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    morkRowObject *rowObj = (morkRowObject *) ioRow;
+    morkRow* row = rowObj->mRowObject_Row;
+    actualPos = MoveRow(ev, row, inHintFromPos, inToPos);
+    outErr = ev->AsErr();
+  }
+  if ( outActualPos )
+    *outActualPos = actualPos;
+  return outErr;
+}
+// } ----- end moving methods -----
+
+// { ----- begin index methods -----
+NS_IMETHODIMP
+morkTable::AddIndex( // create a sorting index for column if possible
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // the column to sort by index
+  nsIMdbThumb** acqThumb) // acquire thumb for incremental index building
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the index addition will be finished.
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::CutIndex( // stop supporting a specific column index
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // the column with index to be removed
+  nsIMdbThumb** acqThumb) // acquire thumb for incremental index destroy
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the index removal will be finished.
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::HasIndex( // query for current presence of a column index
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // the column to investigate
+  mdb_bool* outHasIndex) // whether column has index for this column
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::EnableIndexOnSort( // create an index for col on first sort
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn) // the column to index if ever sorted
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::QueryIndexOnSort( // check whether index on sort is enabled
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn, // the column to investigate
+  mdb_bool* outIndexOnSort) // whether column has index-on-sort enabled
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::DisableIndexOnSort( // prevent future index creation on sort
+  nsIMdbEnv* mev, // context
+  mdb_column inColumn) // the column to index if ever sorted
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end index methods -----
+
+// } ===== end nsIMdbTable methods =====
+
+// we override these so that we'll use the xpcom add and release ref.
+mork_refs
+morkTable::AddStrongRef(morkEnv *ev)
+{
+  return (mork_refs) AddRef();
+}
+
+mork_refs
+morkTable::CutStrongRef(morkEnv *ev)
+{
+  return (mork_refs) Release();
+}
+
+mork_u2
+morkTable::AddTableGcUse(morkEnv* ev)
+{
+  MORK_USED_1(ev); 
+  if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not already maxed out?
+    ++mTable_GcUses;
+    
+  return mTable_GcUses;
+}
+
+mork_u2
+morkTable::CutTableGcUse(morkEnv* ev)
+{
+  if ( mTable_GcUses ) // any outstanding uses to cut?
+  {
+    if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not frozen at max?
+      --mTable_GcUses;
+  }
+  else
+    this->TableGcUsesUnderflowWarning(ev);
+    
+  return mTable_GcUses;
+}
+
+// table dirty handling more complex thatn morkNode::SetNodeDirty() etc.
+
+void morkTable::SetTableClean(morkEnv* ev)
+{
+  if ( mTable_ChangeList.HasListMembers() )
+  {
+    nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+    mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes
+  }
+  mTable_ChangesCount = 0;
+  
+  mTable_Flags = 0;
+  this->SetNodeClean();
+}
+
+// notifications regarding table changes:
+
+void morkTable::NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos)
+{
+  nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+  if ( this->IsTableRewrite() || this->HasChangeOverflow() )
+    this->NoteTableSetAll(ev);
+  else
+  {
+    morkTableChange* tableChange = new(*heap, ev)
+      morkTableChange(ev, ioRow, inPos);
+    if ( tableChange )
+    {
+      if ( ev->Good() )
+      {
+        mTable_ChangeList.PushTail(tableChange);
+        ++mTable_ChangesCount;
+      }
+      else
+      {
+        tableChange->ZapOldNext(ev, heap);
+        this->SetTableRewrite(); // just plan to write all table rows
+      }
+    }
+  }
+}
+
+void morkTable::note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos)
+{
+  if ( this->IsTableRewrite() || this->HasChangeOverflow() )
+    this->NoteTableSetAll(ev);
+  else
+  {
+    nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+    morkTableChange* tableChange = new(*heap, ev)
+      morkTableChange(ev, ioRow, inNewPos);
+    if ( tableChange )
+    {
+      if ( ev->Good() )
+      {
+        mTable_ChangeList.PushTail(tableChange);
+        ++mTable_ChangesCount;
+      }
+      else
+      {
+        tableChange->ZapOldNext(ev, heap);
+        this->NoteTableSetAll(ev);
+      }
+    }
+  }
+}
+
+void morkTable::note_row_change(morkEnv* ev, mork_change inChange,
+  morkRow* ioRow)
+{
+  if ( this->IsTableRewrite() || this->HasChangeOverflow() )
+    this->NoteTableSetAll(ev);
+  else
+  {
+    nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+    morkTableChange* tableChange = new(*heap, ev)
+      morkTableChange(ev, inChange, ioRow);
+    if ( tableChange )
+    {
+      if ( ev->Good() )
+      {
+        mTable_ChangeList.PushTail(tableChange);
+        ++mTable_ChangesCount;
+      }
+      else
+      {
+        tableChange->ZapOldNext(ev, heap);
+        this->NoteTableSetAll(ev);
+      }
+    }
+  }
+}
+
+void morkTable::NoteTableSetAll(morkEnv* ev)
+{
+  if ( mTable_ChangeList.HasListMembers() )
+  {
+    nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+    mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes
+  }
+  mTable_ChangesCount = 0;
+  this->SetTableRewrite();
+}
+
+/*static*/ void
+morkTable::TableGcUsesUnderflowWarning(morkEnv* ev)
+{
+  ev->NewWarning("mTable_GcUses underflow");
+}
+
+/*static*/ void
+morkTable::NonTableTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkTable");
+}
+
+/*static*/ void
+morkTable::NonTableTypeWarning(morkEnv* ev)
+{
+  ev->NewWarning("non morkTable");
+}
+
+/*static*/ void
+morkTable::NilRowSpaceError(morkEnv* ev)
+{
+  ev->NewError("nil mTable_RowSpace");
+}
+
+mork_bool morkTable::MaybeDirtySpaceStoreAndTable()
+{
+  morkRowSpace* rowSpace = mTable_RowSpace;
+  if ( rowSpace )
+  {
+    morkStore* store = rowSpace->mSpace_Store;
+    if ( store && store->mStore_CanDirty )
+    {
+      store->SetStoreDirty();
+      rowSpace->mSpace_CanDirty = morkBool_kTrue;
+    }
+    
+    if ( rowSpace->mSpace_CanDirty ) // first time being dirtied?
+    {
+      if ( this->IsTableClean() )
+      {
+        mork_count rowCount = this->GetRowCount();
+        mork_count oneThird = rowCount / 4; // one third of rows
+        if ( oneThird > 0x07FFF ) // more than half max u2?
+          oneThird = 0x07FFF;
+          
+        mTable_ChangesMax = (mork_u2) oneThird;
+      }
+      this->SetTableDirty();
+      rowSpace->SetRowSpaceDirty();
+      
+      return morkBool_kTrue;
+    }
+  }
+  return morkBool_kFalse;
+}
+
+morkRow*
+morkTable::GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid)
+{
+  morkRow* outRow = mTable_MetaRow;
+  if ( !outRow )
+  {
+    morkStore* store = mTable_Store;
+    mdbOid* oid = &mTable_MetaRowOid;
+    if ( inOptionalMetaRowOid && !oid->mOid_Scope )
+      *oid = *inOptionalMetaRowOid;
+      
+    if ( oid->mOid_Scope ) // oid already recorded in table?
+      outRow = store->OidToRow(ev, oid);
+    else
+    {
+      outRow = store->NewRow(ev, morkStore_kMetaScope);
+      if ( outRow ) // need to record new oid in table?
+        *oid = outRow->mRow_Oid;
+    }
+    mTable_MetaRow = outRow;
+    if ( outRow ) // need to note another use of this row?
+    {
+      outRow->AddRowGcUse(ev);
+
+      this->SetTableNewMeta();
+      if ( this->IsTableClean() ) // catch dirty status of meta row?
+        this->MaybeDirtySpaceStoreAndTable();
+    }
+  }
+  
+  return outRow;
+}
+
+void
+morkTable::GetTableOid(morkEnv* ev, mdbOid* outOid)
+{
+  morkRowSpace* space = mTable_RowSpace;
+  if ( space )
+  {
+    outOid->mOid_Scope = space->SpaceScope();
+    outOid->mOid_Id = this->TableId();
+  }
+  else
+    this->NilRowSpaceError(ev);
+}
+
+nsIMdbTable*
+morkTable::AcquireTableHandle(morkEnv* ev)
+{
+  AddRef();
+  return this;
+}
+
+mork_pos
+morkTable::ArrayHasOid(morkEnv* ev, const mdbOid* inOid)
+{
+  MORK_USED_1(ev); 
+  mork_count count = mTable_RowArray.mArray_Fill;
+  mork_pos pos = -1;
+  while ( ++pos < (mork_pos)count )
+  {
+    morkRow* row = (morkRow*) mTable_RowArray.At(pos);
+    MORK_ASSERT(row);
+    if ( row && row->EqualOid(inOid) )
+    {
+      return pos;
+    }
+  }
+  return -1;
+}
+
+mork_bool
+morkTable::MapHasOid(morkEnv* ev, const mdbOid* inOid)
+{
+  if ( mTable_RowMap )
+    return ( mTable_RowMap->GetOid(ev, inOid) != 0 );
+  else
+    return ( ArrayHasOid(ev, inOid) >= 0 );
+}
+
+void morkTable::build_row_map(morkEnv* ev)
+{
+  morkRowMap* map = mTable_RowMap;
+  if ( !map )
+  {
+    mork_count count = mTable_RowArray.mArray_Fill + 3;
+    nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+    map = new(*heap, ev) morkRowMap(ev, morkUsage::kHeap, heap, heap, count);
+    if ( map )
+    {
+      if ( ev->Good() )
+      {
+        mTable_RowMap = map; // put strong ref here
+        count = mTable_RowArray.mArray_Fill;
+        mork_pos pos = -1;
+        while ( ++pos < (mork_pos)count )
+        {
+          morkRow* row = (morkRow*) mTable_RowArray.At(pos);
+          if ( row && row->IsRow() )
+            map->AddRow(ev, row);
+          else
+            row->NonRowTypeError(ev);
+        }
+      }
+      else
+        map->CutStrongRef(ev);
+    }
+  }
+}
+
+morkRow* morkTable::find_member_row(morkEnv* ev, morkRow* ioRow)
+{
+  if ( mTable_RowMap )
+    return mTable_RowMap->GetRow(ev, ioRow);
+  else
+  {
+    mork_count count = mTable_RowArray.mArray_Fill;
+    mork_pos pos = -1;
+    while ( ++pos < (mork_pos)count )
+    {
+      morkRow* row = (morkRow*) mTable_RowArray.At(pos);
+      if ( row == ioRow )
+        return row;
+    }
+  }
+  return (morkRow*) 0;
+}
+
+mork_pos
+morkTable::MoveRow(morkEnv* ev, morkRow* ioRow, // change row position
+  mork_pos inHintFromPos, // suggested hint regarding start position
+  mork_pos inToPos) // desired new position for row ioRow
+  // MoveRow() returns the actual position of ioRow afterwards; this
+  // position is -1 if and only if ioRow was not found as a member.     
+{
+  mork_pos outPos = -1; // means ioRow was not a table member
+  mork_bool canDirty = ( this->IsTableClean() )?
+    this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
+  
+  morkRow** rows = (morkRow**) mTable_RowArray.mArray_Slots;
+  mork_count count = mTable_RowArray.mArray_Fill;
+  if ( count && rows && ev->Good() ) // any members at all? no errors?
+  {
+    mork_pos lastPos = count - 1; // index of last row slot
+      
+    if ( inToPos > lastPos ) // beyond last used array slot?
+      inToPos = lastPos; // put row into last available slot
+    else if ( inToPos < 0 ) // before first usable slot?
+      inToPos = 0; // put row in very first slow
+      
+    if ( inHintFromPos > lastPos ) // beyond last used array slot?
+      inHintFromPos = lastPos; // seek row in last available slot
+    else if ( inHintFromPos < 0 ) // before first usable slot?
+      inHintFromPos = 0; // seek row in very first slow
+
+    morkRow** fromSlot = 0; // becomes nonzero of ioRow is ever found
+    morkRow** rowsEnd = rows + count; // one past last used array slot
+    
+    if ( inHintFromPos <= 0 ) // start of table? just scan for row?
+    {
+      morkRow** cursor = rows - 1; // before first array slot
+      while ( ++cursor < rowsEnd )
+      {
+        if ( *cursor == ioRow )
+        {
+          fromSlot = cursor;
+          break; // end while loop
+        }
+      }
+    }
+    else // search near the start position and work outwards
+    {
+      morkRow** lo = rows + inHintFromPos; // lowest search point
+      morkRow** hi = lo; // highest search point starts at lowest point
+      
+      // Seek ioRow in spiral widening search below and above inHintFromPos.
+      // This is faster when inHintFromPos is at all accurate, but is slower
+      // than a straightforward scan when inHintFromPos is nearly random.
+      
+      while ( lo >= rows || hi < rowsEnd ) // keep searching?
+      {
+        if ( lo >= rows ) // low direction search still feasible?
+        {
+          if ( *lo == ioRow ) // actually found the row?
+          {
+            fromSlot = lo;
+            break; // end while loop
+          }
+          --lo; // advance further lower
+        }
+        if ( hi < rowsEnd ) // high direction search still feasible?
+        {
+          if ( *hi == ioRow ) // actually found the row?
+          {
+            fromSlot = hi;
+            break; // end while loop
+          }
+          ++hi; // advance further higher
+        }
+      }
+    }
+    
+    if ( fromSlot ) // ioRow was found as a table member?
+    {
+      outPos = fromSlot - rows; // actual position where row was found
+      if ( outPos != inToPos ) // actually need to move this row?
+      {
+        morkRow** toSlot = rows + inToPos; // slot where row must go
+        
+        ++mTable_RowArray.mArray_Seed; // we modify the array now:
+        
+        if ( fromSlot < toSlot ) // row is moving upwards?
+        {
+          morkRow** up = fromSlot; // leading pointer going upward
+          while ( ++up <= toSlot ) // have not gone above destination?
+          {
+            *fromSlot = *up; // shift down one
+            fromSlot = up; // shift trailing pointer up
+          }
+        }
+        else // ( fromSlot > toSlot ) // row is moving downwards
+        {
+          morkRow** down = fromSlot; // leading pointer going downward
+          while ( --down >= toSlot ) // have not gone below destination?
+          {
+            *fromSlot = *down; // shift up one
+            fromSlot = down; // shift trailing pointer
+          }
+        }
+        *toSlot = ioRow;
+        outPos = inToPos; // okay, we actually moved the row here
+
+        if ( canDirty )
+          this->note_row_move(ev, ioRow, inToPos);
+      }
+    }
+  }
+  return outPos;
+}
+
+mork_bool
+morkTable::AddRow(morkEnv* ev, morkRow* ioRow)
+{
+  morkRow* row = this->find_member_row(ev, ioRow);
+  if ( !row && ev->Good() )
+  {
+    mork_bool canDirty = ( this->IsTableClean() )?
+      this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
+      
+    mork_pos pos = mTable_RowArray.AppendSlot(ev, ioRow);
+    if ( ev->Good() && pos >= 0 )
+    {
+      ioRow->AddRowGcUse(ev);
+      if ( mTable_RowMap )
+      {
+        if ( mTable_RowMap->AddRow(ev, ioRow) )
+        {
+          // okay, anything else?
+        }
+        else
+          mTable_RowArray.CutSlot(ev, pos);
+      }
+      else if ( mTable_RowArray.mArray_Fill >= morkTable_kMakeRowMapThreshold )
+        this->build_row_map(ev);
+
+      if ( canDirty && ev->Good() )
+        this->NoteTableAddRow(ev, ioRow);
+    }
+  }
+  return ev->Good();
+}
+
+mork_bool
+morkTable::CutRow(morkEnv* ev, morkRow* ioRow)
+{
+  morkRow* row = this->find_member_row(ev, ioRow);
+  if ( row )
+  {
+    mork_bool canDirty = ( this->IsTableClean() )?
+      this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
+      
+    mork_count count = mTable_RowArray.mArray_Fill;
+    morkRow** rowSlots = (morkRow**) mTable_RowArray.mArray_Slots;
+    if ( rowSlots ) // array has vector as expected?
+    {
+      mork_pos pos = -1;
+      morkRow** end = rowSlots + count;
+      morkRow** slot = rowSlots - 1; // prepare for preincrement:
+      while ( ++slot < end ) // another slot to check?
+      {
+        if ( *slot == row ) // found the slot containing row?
+        {
+          pos = slot - rowSlots; // record absolute position
+          break; // end while loop
+        }
+      }
+      if ( pos >= 0 ) // need to cut if from the array?
+        mTable_RowArray.CutSlot(ev, pos);
+      else
+        ev->NewWarning("row not found in array");
+    }
+    else
+      mTable_RowArray.NilSlotsAddressError(ev);
+      
+    if ( mTable_RowMap )
+      mTable_RowMap->CutRow(ev, ioRow);
+
+    if ( canDirty )
+      this->NoteTableCutRow(ev, ioRow);
+
+    if ( ioRow->CutRowGcUse(ev) == 0 )
+      ioRow->OnZeroRowGcUse(ev);
+  }
+  return ev->Good();
+}
+
+
+mork_bool
+morkTable::CutAllRows(morkEnv* ev)
+{
+  if ( this->MaybeDirtySpaceStoreAndTable() )
+  {
+    this->SetTableRewrite(); // everything is dirty
+    this->NoteTableSetAll(ev);
+  }
+    
+  if ( ev->Good() )
+  {
+    mTable_RowArray.CutAllSlots(ev);
+    if ( mTable_RowMap )
+    {
+      morkRowMapIter i(ev, mTable_RowMap);
+      mork_change* c = 0;
+      morkRow* r = 0;
+      
+      for ( c = i.FirstRow(ev, &r); c;  c = i.NextRow(ev, &r) )
+      {
+        if ( r )
+        {
+          if ( r->CutRowGcUse(ev) == 0 )
+            r->OnZeroRowGcUse(ev);
+            
+          i.CutHereRow(ev, (morkRow**) 0);
+        }
+        else
+          ev->NewWarning("nil row in table map");
+      }
+    }
+  }
+  return ev->Good();
+}
+
+morkTableRowCursor*
+morkTable::NewTableRowCursor(morkEnv* ev, mork_pos inRowPos)
+{
+  morkTableRowCursor* outCursor = 0;
+  if ( ev->Good() )
+  {
+    nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+    morkTableRowCursor* cursor = new(*heap, ev)
+      morkTableRowCursor(ev, morkUsage::kHeap, heap, this, inRowPos);
+    if ( cursor )
+    {
+      if ( ev->Good() )
+        outCursor = cursor;
+      else
+        cursor->CutStrongRef((nsIMdbEnv *) ev);
+    }
+  }
+  return outCursor;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+morkTableChange::morkTableChange(morkEnv* ev, mork_change inChange,
+  morkRow* ioRow)
+// use this constructor for inChange == morkChange_kAdd or morkChange_kCut
+: morkNext()
+, mTableChange_Row( ioRow )
+, mTableChange_Pos( morkTableChange_kNone )
+{
+  if ( ioRow )
+  {
+    if ( ioRow->IsRow() )
+    {
+      if ( inChange == morkChange_kAdd )
+        mTableChange_Pos = morkTableChange_kAdd;
+      else if ( inChange == morkChange_kCut )
+        mTableChange_Pos = morkTableChange_kCut;
+      else
+        this->UnknownChangeError(ev);
+    }
+    else
+      ioRow->NonRowTypeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+morkTableChange::morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos)
+// use this constructor when the row is moved
+: morkNext()
+, mTableChange_Row( ioRow )
+, mTableChange_Pos( inPos )
+{
+  if ( ioRow )
+  {
+    if ( ioRow->IsRow() )
+    {
+      if ( inPos < 0 )
+        this->NegativeMovePosError(ev);
+    }
+    else
+      ioRow->NonRowTypeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+void morkTableChange::UnknownChangeError(morkEnv* ev) const
+// morkChange_kAdd or morkChange_kCut
+{
+  ev->NewError("mTableChange_Pos neither kAdd nor kCut");
+}
+
+void morkTableChange::NegativeMovePosError(morkEnv* ev) const
+// move must be non-neg position
+{
+  ev->NewError("negative mTableChange_Pos for row move");
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+morkTableMap::~morkTableMap()
+{
+}
+
+morkTableMap::morkTableMap(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+  : morkBeadMap(ev, inUsage, ioHeap, ioSlotHeap)
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+  : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kTableMap;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkTable.h
@@ -0,0 +1,753 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKTABLE_
+#define _MORKTABLE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkArray.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+#ifndef _MORKBEAD_
+#include "morkBead.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class nsIMdbTable;
+#define morkDerived_kTable  /*i*/ 0x5462 /* ascii 'Tb' */
+
+/*| kStartRowArraySize: starting physical size of array for mTable_RowArray.
+**| We want this number very small, so that a table containing exactly one
+**| row member will not pay too significantly in space overhead.  But we want
+**| a number bigger than one, so there is some space for growth.
+|*/
+#define morkTable_kStartRowArraySize 3 /* modest starting size for array */
+
+/*| kMakeRowMapThreshold: this is the number of rows in a table which causes
+**| a hash table (mTable_RowMap) to be lazily created for faster member row
+**| identification, during such operations as cuts and adds.  This number must
+**| be small enough that linear searches are not bad for member counts less
+**| than this; but this number must also be large enough that creating a hash
+**| table does not increase the per-row space overhead by a big percentage.
+**| For speed, numbers on the order of ten to twenty are all fine; for space,
+**| I believe a number as small as ten will have too much space overhead.
+|*/
+#define morkTable_kMakeRowMapThreshold 17 /* when to build mTable_RowMap */
+
+#define morkTable_kStartRowMapSlotCount 13
+#define morkTable_kMaxTableGcUses 0x0FF /* max for 8-bit unsigned int */
+
+#define morkTable_kUniqueBit   ((mork_u1) (1 << 0))
+#define morkTable_kVerboseBit  ((mork_u1) (1 << 1))
+#define morkTable_kNotedBit    ((mork_u1) (1 << 2)) /* space has change notes */
+#define morkTable_kRewriteBit  ((mork_u1) (1 << 3)) /* must rewrite all rows */
+#define morkTable_kNewMetaBit  ((mork_u1) (1 << 4)) /* new table meta row */
+
+class morkTable : public morkObject, public morkLink, public nsIMdbTable { 
+
+  // NOTE the morkLink base is for morkRowSpace::mRowSpace_TablesByPriority
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*    mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+  // mork_color   mBead_Color;   // ID for this bead
+  // morkHandle*  mObject_Handle;  // weak ref to handle for this object
+
+public: // bead color setter & getter replace obsolete member mTable_Id:
+
+  NS_DECL_ISUPPORTS_INHERITED
+  mork_tid     TableId() const { return mBead_Color; }
+  void         SetTableId(mork_tid inTid) { mBead_Color = inTid; }
+
+  // we override these so we use xpcom ref-counting semantics.
+  virtual mork_refs    AddStrongRef(morkEnv* ev);
+  virtual mork_refs    CutStrongRef(morkEnv* ev);
+public: // state is public because the entire Mork system is private
+
+// { ===== begin nsIMdbCollection methods =====
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetSeed(nsIMdbEnv* ev,
+    mdb_seed* outSeed);    // member change count
+  NS_IMETHOD GetCount(nsIMdbEnv* ev,
+    mdb_count* outCount); // member count
+
+  NS_IMETHOD GetPort(nsIMdbEnv* ev,
+    nsIMdbPort** acqPort); // collection container
+  // } ----- end attribute methods -----
+
+  // { ----- begin cursor methods -----
+  NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos
+    nsIMdbEnv* ev, // context
+    mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+    nsIMdbCursor** acqCursor); // acquire new cursor instance
+  // } ----- end cursor methods -----
+
+  // { ----- begin ID methods -----
+  NS_IMETHOD GetOid(nsIMdbEnv* ev,
+    mdbOid* outOid); // read object identity
+  NS_IMETHOD BecomeContent(nsIMdbEnv* ev,
+    const mdbOid* inOid); // exchange content
+  // } ----- end ID methods -----
+
+  // { ----- begin activity dropping methods -----
+  NS_IMETHOD DropActivity( // tell collection usage no longer expected
+    nsIMdbEnv* ev);
+  // } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+  NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio);
+  NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio);
+  
+  NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose);
+  NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose);
+  
+  NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique);
+  
+  NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind);
+  NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope);
+  
+  NS_IMETHOD GetMetaRow(
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+    mdbOid* outOid, // output meta row oid, can be nil to suppress output
+    nsIMdbRow** acqRow); // acquire table's unique singleton meta row
+    // The purpose of a meta row is to support the persistent recording of
+    // meta info about a table as cells put into the distinguished meta row.
+    // Each table has exactly one meta row, which is not considered a member
+    // of the collection of rows inside the table.  The only way to tell
+    // whether a row is a meta row is by the fact that it is returned by this
+    // GetMetaRow() method from some table. Otherwise nothing distinguishes
+    // a meta row from any other row.  A meta row can be used anyplace that
+    // any other row can be used, and can even be put into other tables (or
+    // the same table) as a table member, if this is useful for some reason.
+    // The first attempt to access a table's meta row using GetMetaRow() will
+    // cause the meta row to be created if it did not already exist.  When the
+    // meta row is created, it will have the row oid that was previously
+    // requested for this table's meta row; or if no oid was ever explicitly
+    // specified for this meta row, then a unique oid will be generated in
+    // the row scope named "m" (so obviously MDB clients should not
+    // manually allocate any row IDs from that special meta scope namespace).
+    // The meta row oid can be specified either when the table is created, or
+    // else the first time that GetMetaRow() is called, by passing a non-nil
+    // pointer to an oid for parameter inOptionalMetaRowOid.  The meta row's
+    // actual oid is returned in outOid (if this is a non-nil pointer), and
+    // it will be different from inOptionalMetaRowOid when the meta row was
+    // already given a different oid earlier.
+  // } ----- end meta attribute methods -----
+
+
+  // { ----- begin cursor methods -----
+  NS_IMETHOD GetTableRowCursor( // make a cursor, starting iteration at inRowPos
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    nsIMdbTableRowCursor** acqCursor); // acquire new cursor instance
+  // } ----- end row position methods -----
+
+  // { ----- begin row position methods -----
+  NS_IMETHOD PosToOid( // get row member for a table position
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    mdbOid* outOid); // row oid at the specified position
+
+  NS_IMETHOD OidToPos( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid, // row to find in table
+    mdb_pos* outPos); // zero-based ordinal position of row in table
+    
+  NS_IMETHOD PosToRow( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    mdb_pos inRowPos, // zero-based ordinal position of row in table
+    nsIMdbRow** acqRow); // acquire row at table position inRowPos
+    
+  NS_IMETHOD RowToPos( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow, // row to find in table
+    mdb_pos* outPos); // zero-based ordinal position of row in table
+  // } ----- end row position methods -----
+
+  // { ----- begin oid set methods -----
+  NS_IMETHOD AddOid( // make sure the row with inOid is a table member 
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid); // row to ensure membership in table
+
+  NS_IMETHOD HasOid( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid, // row to find in table
+    mdb_bool* outHasOid); // whether inOid is a member row
+
+  NS_IMETHOD CutOid( // make sure the row with inOid is not a member 
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid); // row to remove from table
+  // } ----- end oid set methods -----
+
+  // { ----- begin row set methods -----
+  NS_IMETHOD NewRow( // create a new row instance in table
+    nsIMdbEnv* ev, // context
+    mdbOid* ioOid, // please use minus one (unbound) rowId for db-assigned IDs
+    nsIMdbRow** acqRow); // create new row
+
+  NS_IMETHOD AddRow( // make sure the row with inOid is a table member 
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow); // row to ensure membership in table
+
+  NS_IMETHOD HasRow( // test for the table position of a row member
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow, // row to find in table
+    mdb_bool* outHasRow); // whether row is a table member
+
+  NS_IMETHOD CutRow( // make sure the row with inOid is not a member 
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow); // row to remove from table
+
+  NS_IMETHOD CutAllRows( // remove all rows from the table
+    nsIMdbEnv* ev); // context
+  // } ----- end row set methods -----
+
+  // { ----- begin hinting methods -----
+  NS_IMETHOD SearchColumnsHint( // advise re future expected search cols  
+    nsIMdbEnv* ev, // context
+    const mdbColumnSet* inColumnSet); // columns likely to be searched
+    
+  NS_IMETHOD SortColumnsHint( // advise re future expected sort columns  
+    nsIMdbEnv* ev, // context
+    const mdbColumnSet* inColumnSet); // columns for likely sort requests
+    
+  NS_IMETHOD StartBatchChangeHint( // advise before many adds and cuts  
+    nsIMdbEnv* ev, // context
+    const void* inLabel); // intend unique address to match end call
+    // If batch starts nest by virtue of nesting calls in the stack, then
+    // the address of a local variable makes a good batch start label that
+    // can be used at batch end time, and such addresses remain unique.
+    
+  NS_IMETHOD EndBatchChangeHint( // advise before many adds and cuts  
+    nsIMdbEnv* ev, // context
+    const void* inLabel); // label matching start label
+    // Suppose a table is maintaining one or many sort orders for a table,
+    // so that every row added to the table must be inserted in each sort,
+    // and every row cut must be removed from each sort.  If a db client
+    // intends to make many such changes before needing any information
+    // about the order or positions of rows inside a table, then a client
+    // might tell the table to start batch changes in order to disable
+    // sorting of rows for the interim.  Presumably a table will then do
+    // a full sort of all rows at need when the batch changes end, or when
+    // a surprise request occurs for row position during batch changes.
+  // } ----- end hinting methods -----
+
+  // { ----- begin searching methods -----
+  NS_IMETHOD FindRowMatches( // search variable number of sorted cols
+    nsIMdbEnv* ev, // context
+    const mdbYarn* inPrefix, // content to find as prefix in row's column cell
+    nsIMdbTableRowCursor** acqCursor); // set of matching rows
+    
+  NS_IMETHOD GetSearchColumns( // query columns used by FindRowMatches()
+    nsIMdbEnv* ev, // context
+    mdb_count* outCount, // context
+    mdbColumnSet* outColSet); // caller supplied space to put columns
+    // GetSearchColumns() returns the columns actually searched when the
+    // FindRowMatches() method is called.  No more than mColumnSet_Count
+    // slots of mColumnSet_Columns will be written, since mColumnSet_Count
+    // indicates how many slots are present in the column array.  The
+    // actual number of search column used by the table is returned in
+    // the outCount parameter; if this number exceeds mColumnSet_Count,
+    // then a caller needs a bigger array to read the entire column set.
+    // The minimum of mColumnSet_Count and outCount is the number slots
+    // in mColumnSet_Columns that were actually written by this method.
+    //
+    // Callers are expected to change this set of columns by calls to
+    // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
+  // } ----- end searching methods -----
+
+  // { ----- begin sorting methods -----
+  // sorting: note all rows are assumed sorted by row ID as a secondary
+  // sort following the primary column sort, when table rows are sorted.
+
+  NS_IMETHOD
+  CanSortColumn( // query which column is currently used for sorting
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // column to query sorting potential
+    mdb_bool* outCanSort); // whether the column can be sorted
+    
+  NS_IMETHOD GetSorting( // view same table in particular sorting
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // requested new column for sorting table
+    nsIMdbSorting** acqSorting); // acquire sorting for column
+    
+  NS_IMETHOD SetSearchSorting( // use this sorting in FindRowMatches()
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
+    nsIMdbSorting* ioSorting); // requested sorting for some column
+    // SetSearchSorting() attempts to inform the table that ioSorting
+    // should be used during calls to FindRowMatches() for searching
+    // the column which is actually sorted by ioSorting.  This method
+    // is most useful in conjunction with nsIMdbSorting::SetCompare(),
+    // because otherwise a caller would not be able to override the
+    // comparison ordering method used during searchs.  Note that some
+    // database implementations might be unable to use an arbitrarily
+    // specified sort order, either due to schema or runtime interface
+    // constraints, in which case ioSorting might not actually be used.
+    // Presumably ioSorting is an instance that was returned from some
+    // earlier call to nsIMdbTable::GetSorting().  A caller can also
+    // use nsIMdbTable::SearchColumnsHint() to specify desired change
+    // in which columns are sorted and searched by FindRowMatches().
+    //
+    // A caller can pass a nil pointer for ioSorting to request that
+    // column inColumn no longer be used at all by FindRowMatches().
+    // But when ioSorting is non-nil, then inColumn should match the
+    // column actually sorted by ioSorting; when these do not agree,
+    // implementations are instructed to give precedence to the column
+    // specified by ioSorting (so this means callers might just pass
+    // zero for inColumn when ioSorting is also provided, since then
+    // inColumn is both redundant and ignored).
+  // } ----- end sorting methods -----
+
+  // { ----- begin moving methods -----
+  // moving a row does nothing unless a table is currently unsorted
+  
+  NS_IMETHOD MoveOid( // change position of row in unsorted table
+    nsIMdbEnv* ev, // context
+    const mdbOid* inOid,  // row oid to find in table
+    mdb_pos inHintFromPos, // suggested hint regarding start position
+    mdb_pos inToPos,       // desired new position for row inRowId
+    mdb_pos* outActualPos); // actual new position of row in table
+
+  NS_IMETHOD MoveRow( // change position of row in unsorted table
+    nsIMdbEnv* ev, // context
+    nsIMdbRow* ioRow,  // row oid to find in table
+    mdb_pos inHintFromPos, // suggested hint regarding start position
+    mdb_pos inToPos,       // desired new position for row inRowId
+    mdb_pos* outActualPos); // actual new position of row in table
+  // } ----- end moving methods -----
+  
+  // { ----- begin index methods -----
+  NS_IMETHOD AddIndex( // create a sorting index for column if possible
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // the column to sort by index
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental index building
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the index addition will be finished.
+  
+  NS_IMETHOD CutIndex( // stop supporting a specific column index
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // the column with index to be removed
+    nsIMdbThumb** acqThumb); // acquire thumb for incremental index destroy
+  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+  // then the index removal will be finished.
+  
+  NS_IMETHOD HasIndex( // query for current presence of a column index
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // the column to investigate
+    mdb_bool* outHasIndex); // whether column has index for this column
+
+  
+  NS_IMETHOD EnableIndexOnSort( // create an index for col on first sort
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn); // the column to index if ever sorted
+  
+  NS_IMETHOD QueryIndexOnSort( // check whether index on sort is enabled
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn, // the column to investigate
+    mdb_bool* outIndexOnSort); // whether column has index-on-sort enabled
+  
+  NS_IMETHOD DisableIndexOnSort( // prevent future index creation on sort
+    nsIMdbEnv* ev, // context
+    mdb_column inColumn); // the column to index if ever sorted
+  // } ----- end index methods -----
+
+  morkStore*      mTable_Store;   // non-refcnted ptr to port
+
+  // mTable_RowSpace->SpaceScope() is row scope 
+  morkRowSpace*   mTable_RowSpace; // non-refcnted ptr to containing space
+
+  morkRow*        mTable_MetaRow; // table's actual meta row
+  mdbOid          mTable_MetaRowOid; // oid for meta row
+  
+  morkRowMap*     mTable_RowMap;     // (strong ref) hash table of all members
+  morkArray       mTable_RowArray;   // array of morkRow pointers
+  
+  morkList        mTable_ChangeList;      // list of table changes
+  mork_u2         mTable_ChangesCount; // length of changes list 
+  mork_u2         mTable_ChangesMax;   // max list length before rewrite 
+  
+  // mork_tid        mTable_Id;
+  mork_kind       mTable_Kind;
+  
+  mork_u1         mTable_Flags;         // bit flags
+  mork_priority   mTable_Priority;      // 0..9, any other value equals 9
+  mork_u1         mTable_GcUses;        // persistent references from cells
+  mork_u1         mTable_Pad;      // for u4 alignment
+
+public: // flags bit twiddling
+  
+  void SetTableUnique() { mTable_Flags |= morkTable_kUniqueBit; }
+  void SetTableVerbose() { mTable_Flags |= morkTable_kVerboseBit; }
+  void SetTableNoted() { mTable_Flags |= morkTable_kNotedBit; }
+  void SetTableRewrite() { mTable_Flags |= morkTable_kRewriteBit; }
+  void SetTableNewMeta() { mTable_Flags |= morkTable_kNewMetaBit; }
+
+  void ClearTableUnique() { mTable_Flags &= (mork_u1) ~morkTable_kUniqueBit; }
+  void ClearTableVerbose() { mTable_Flags &= (mork_u1) ~morkTable_kVerboseBit; }
+  void ClearTableNoted() { mTable_Flags &= (mork_u1) ~morkTable_kNotedBit; }
+  void ClearTableRewrite() { mTable_Flags &= (mork_u1) ~morkTable_kRewriteBit; }
+  void ClearTableNewMeta() { mTable_Flags &= (mork_u1) ~morkTable_kNewMetaBit; }
+
+  mork_bool IsTableUnique() const
+  { return ( mTable_Flags & morkTable_kUniqueBit ) != 0; }
+  
+  mork_bool IsTableVerbose() const
+  { return ( mTable_Flags & morkTable_kVerboseBit ) != 0; }
+  
+  mork_bool IsTableNoted() const
+  { return ( mTable_Flags & morkTable_kNotedBit ) != 0; }
+  
+  mork_bool IsTableRewrite() const
+  { return ( mTable_Flags & morkTable_kRewriteBit ) != 0; }
+  
+  mork_bool IsTableNewMeta() const
+  { return ( mTable_Flags & morkTable_kNewMetaBit ) != 0; }
+
+public: // table dirty handling more complex than morkNode::SetNodeDirty() etc.
+
+  void SetTableDirty() { this->SetNodeDirty(); }
+  void SetTableClean(morkEnv* ev);
+   
+  mork_bool IsTableClean() const { return this->IsNodeClean(); }
+  mork_bool IsTableDirty() const { return this->IsNodeDirty(); }
+
+public: // morkNode memory management operators
+  void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+  { return morkNode::MakeNew(inSize, ioHeap, ev); }
+  
+ 
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseTable() if open
+  virtual ~morkTable(); // assert that close executed earlier
+  
+public: // morkTable construction & destruction
+  morkTable(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioNodeHeap, morkStore* ioStore,
+    nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace,
+    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
+    mork_tid inTableId,
+    mork_kind inKind, mork_bool inMustBeUnique);
+  void CloseTable(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkTable(const morkTable& other);
+  morkTable& operator=(const morkTable& other);
+
+public: // dynamic type identification
+  mork_bool IsTable() const
+  { return IsNode() && mNode_Derived == morkDerived_kTable; }
+// } ===== end morkNode methods =====
+
+public: // errors
+  static void NonTableTypeError(morkEnv* ev);
+  static void NonTableTypeWarning(morkEnv* ev);
+  static void NilRowSpaceError(morkEnv* ev);
+
+public: // warnings
+  static void TableGcUsesUnderflowWarning(morkEnv* ev);
+
+public: // noting table changes
+
+  mork_bool HasChangeOverflow() const
+  { return mTable_ChangesCount >= mTable_ChangesMax; }
+
+  void NoteTableSetAll(morkEnv* ev);
+  void NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos);
+
+  void note_row_change(morkEnv* ev, mork_change inChange, morkRow* ioRow);
+  void note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos);
+  
+  void NoteTableAddRow(morkEnv* ev, morkRow* ioRow)
+  { this->note_row_change(ev, morkChange_kAdd, ioRow); }
+  
+  void NoteTableCutRow(morkEnv* ev, morkRow* ioRow)
+  { this->note_row_change(ev, morkChange_kCut, ioRow); }
+  
+protected: // internal row map methods
+
+  morkRow* find_member_row(morkEnv* ev, morkRow* ioRow);
+  void build_row_map(morkEnv* ev);
+
+public: // other table methods
+  
+  mork_bool MaybeDirtySpaceStoreAndTable();
+
+  morkRow* GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid);
+  
+  mork_u2 AddTableGcUse(morkEnv* ev);
+  mork_u2 CutTableGcUse(morkEnv* ev);
+
+  // void DirtyAllTableContent(morkEnv* ev);
+
+  mork_seed TableSeed() const { return mTable_RowArray.mArray_Seed; }
+  
+  morkRow* SafeRowAt(morkEnv* ev, mork_pos inPos)
+  { return (morkRow*) mTable_RowArray.SafeAt(ev, inPos); }
+
+  nsIMdbTable* AcquireTableHandle(morkEnv* ev); // mObject_Handle
+  
+  mork_count GetRowCount() const { return mTable_RowArray.mArray_Fill; }
+
+  mork_bool IsTableUsed() const
+  { return (mTable_GcUses != 0 || this->GetRowCount() != 0); }
+
+  void GetTableOid(morkEnv* ev, mdbOid* outOid);
+  mork_pos  ArrayHasOid(morkEnv* ev, const mdbOid* inOid);
+  mork_bool MapHasOid(morkEnv* ev, const mdbOid* inOid);
+  mork_bool AddRow(morkEnv* ev, morkRow* ioRow); // returns ev->Good()
+  mork_bool CutRow(morkEnv* ev, morkRow* ioRow); // returns ev->Good()
+  mork_bool CutAllRows(morkEnv* ev); // returns ev->Good()
+  
+  mork_pos MoveRow(morkEnv* ev, morkRow* ioRow, // change row position
+    mork_pos inHintFromPos, // suggested hint regarding start position
+    mork_pos inToPos); // desired new position for row ioRow
+    // MoveRow() returns the actual position of ioRow afterwards; this
+    // position is -1 if and only if ioRow was not found as a member.     
+
+
+  morkTableRowCursor* NewTableRowCursor(morkEnv* ev, mork_pos inRowPos);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakTable(morkTable* me,
+    morkEnv* ev, morkTable** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongTable(morkTable* me,
+    morkEnv* ev, morkTable** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// use negative values for kCut and kAdd, to keep non-neg move pos distinct:
+#define morkTableChange_kCut ((mork_pos) -1) /* shows row was cut */
+#define morkTableChange_kAdd ((mork_pos) -2) /* shows row was added */
+#define morkTableChange_kNone ((mork_pos) -3) /* unknown change */
+
+class morkTableChange : public morkNext { 
+public: // state is public because the entire Mork system is private
+
+  morkRow*  mTableChange_Row; // the row in the change
+  
+  mork_pos  mTableChange_Pos; // kAdd, kCut, or non-neg for row move
+
+public:
+  morkTableChange(morkEnv* ev, mork_change inChange, morkRow* ioRow);
+  // use this constructor for inChange == morkChange_kAdd or morkChange_kCut
+  
+  morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos);
+  // use this constructor when the row is moved
+
+public:
+  void UnknownChangeError(morkEnv* ev) const; // morkChange_kAdd or morkChange_kCut
+  void NegativeMovePosError(morkEnv* ev) const; // move must be non-neg position
+  
+public:
+  
+  mork_bool IsAddRowTableChange() const
+  { return ( mTableChange_Pos == morkTableChange_kAdd ); }
+  
+  mork_bool IsCutRowTableChange() const
+  { return ( mTableChange_Pos == morkTableChange_kCut ); }
+  
+  mork_bool IsMoveRowTableChange() const
+  { return ( mTableChange_Pos >= 0 ); }
+
+public:
+  
+  mork_pos GetMovePos() const { return mTableChange_Pos; }
+  // GetMovePos() assumes that IsMoveRowTableChange() is true.
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kTableMap  /*i*/ 0x744D /* ascii 'tM' */
+
+/*| morkTableMap: maps mork_token -> morkTable
+|*/
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+class morkTableMap : public morkBeadMap { 
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+class morkTableMap : public morkNodeMap { // for mapping tokens to tables
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+
+public:
+
+  virtual ~morkTableMap();
+  morkTableMap(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+
+public: // other map methods
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+  mork_bool  AddTable(morkEnv* ev, morkTable* ioTable)
+  { return this->AddBead(ev, ioTable); }
+  // the AddTable() boolean return equals ev->Good().
+
+  mork_bool  CutTable(morkEnv* ev, mork_tid inTid)
+  { return this->CutBead(ev, inTid); }
+  // The CutTable() boolean return indicates whether removal happened. 
+  
+  morkTable*  GetTable(morkEnv* ev, mork_tid inTid)
+  { return (morkTable*) this->GetBead(ev, inTid); }
+  // Note the returned table does NOT have an increase in refcount for this.
+
+  mork_num CutAllTables(morkEnv* ev)
+  { return this->CutAllBeads(ev); }
+  // CutAllTables() releases all the referenced table values.
+  
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+  mork_bool  AddTable(morkEnv* ev, morkTable* ioTable)
+  { return this->AddNode(ev, ioTable->TableId(), ioTable); }
+  // the AddTable() boolean return equals ev->Good().
+
+  mork_bool  CutTable(morkEnv* ev, mork_tid inTid)
+  { return this->CutNode(ev, inTid); }
+  // The CutTable() boolean return indicates whether removal happened. 
+  
+  morkTable*  GetTable(morkEnv* ev, mork_tid inTid)
+  { return (morkTable*) this->GetNode(ev, inTid); }
+  // Note the returned table does NOT have an increase in refcount for this.
+
+  mork_num CutAllTables(morkEnv* ev)
+  { return this->CutAllNodes(ev); }
+  // CutAllTables() releases all the referenced table values.
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+
+};
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+class morkTableMapIter: public morkBeadMapIter {
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+class morkTableMapIter: public morkMapIter{ // typesafe wrapper class
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+
+public:
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+  morkTableMapIter(morkEnv* ev, morkTableMap* ioMap)
+  : morkBeadMapIter(ev, ioMap) { }
+ 
+  morkTableMapIter( ) : morkBeadMapIter()  { }
+  void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap)
+  { this->InitBeadMapIter(ev, ioMap); }
+   
+  morkTable* FirstTable(morkEnv* ev)
+  { return (morkTable*) this->FirstBead(ev); }
+  
+  morkTable* NextTable(morkEnv* ev)
+  { return (morkTable*) this->NextBead(ev); }
+  
+  morkTable* HereTable(morkEnv* ev)
+  { return (morkTable*) this->HereBead(ev); }
+  
+
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+  morkTableMapIter(morkEnv* ev, morkTableMap* ioMap)
+  : morkMapIter(ev, ioMap) { }
+ 
+  morkTableMapIter( ) : morkMapIter()  { }
+  void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap)
+  { this->InitMapIter(ev, ioMap); }
+   
+  mork_change*
+  FirstTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable)
+  { return this->First(ev, outTid, outTable); }
+  
+  mork_change*
+  NextTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable)
+  { return this->Next(ev, outTid, outTable); }
+  
+  mork_change*
+  HereTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable)
+  { return this->Here(ev, outTid, outTable); }
+  
+  // cutting while iterating hash map might dirty the parent table:
+  mork_change*
+  CutHereTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable)
+  { return this->CutHere(ev, outTid, outTable); }
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTABLE_ */
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkTableRowCursor.cpp
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Blake Ross (blake@blakeross.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKTABLEROWCURSOR_
+#include "morkTableRowCursor.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkTableRowCursor::CloseMorkNode(morkEnv* ev) // CloseTableRowCursor() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseTableRowCursor(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkTableRowCursor::~morkTableRowCursor() // CloseTableRowCursor() executed earlier
+{
+  CloseMorkNode(mMorkEnv);
+  MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkTableRowCursor::morkTableRowCursor(morkEnv* ev,
+  const morkUsage& inUsage,
+  nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos)
+: morkCursor(ev, inUsage, ioHeap)
+, mTableRowCursor_Table( 0 )
+{
+  if ( ev->Good() )
+  {
+    if ( ioTable )
+    {
+      mCursor_Pos = inRowPos;
+      mCursor_Seed = ioTable->TableSeed();
+      morkTable::SlotWeakTable(ioTable, ev, &mTableRowCursor_Table);
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kTableRowCursor;
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkTableRowCursor, morkCursor, nsIMdbTableRowCursor)
+/*public non-poly*/ void
+morkTableRowCursor::CloseTableRowCursor(morkEnv* ev) 
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mCursor_Pos = -1;
+      mCursor_Seed = 0;
+      morkTable::SlotWeakTable((morkTable*) 0, ev, &mTableRowCursor_Table);
+      this->CloseCursor(ev);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+// { ----- begin attribute methods -----
+/*virtual*/ mdb_err
+morkTableRowCursor::GetCount(nsIMdbEnv* mev, mdb_count* outCount)
+{
+  mdb_err outErr = 0;
+  mdb_count count = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    count = GetMemberCount(ev);
+    outErr = ev->AsErr();
+  }
+  if ( outCount )
+    *outCount = count;
+  return outErr;
+}
+
+/*virtual*/ mdb_err
+morkTableRowCursor::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed)
+{
+  NS_ASSERTION(PR_FALSE, "not implemented");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/*virtual*/ mdb_err
+morkTableRowCursor::SetPos(nsIMdbEnv* mev, mdb_pos inPos)
+{
+  mCursor_Pos = inPos;
+  return NS_OK;
+}
+
+/*virtual*/ mdb_err
+morkTableRowCursor::GetPos(nsIMdbEnv* mev, mdb_pos* outPos)
+{
+  *outPos = mCursor_Pos;
+  return NS_OK;
+}
+
+/*virtual*/ mdb_err
+morkTableRowCursor::SetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool inFail)
+{
+  mCursor_DoFailOnSeedOutOfSync = inFail;
+  return NS_OK;
+}
+
+/*virtual*/ mdb_err
+morkTableRowCursor::GetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool* outFail)
+{
+  NS_ENSURE_ARG_POINTER(outFail);
+  *outFail = mCursor_DoFailOnSeedOutOfSync;
+  return NS_OK;
+}
+// } ----- end attribute methods -----
+
+
+// { ===== begin nsIMdbTableRowCursor methods =====
+
+// { ----- begin attribute methods -----
+
+NS_IMETHODIMP
+morkTableRowCursor::GetTable(nsIMdbEnv* mev, nsIMdbTable** acqTable)
+{
+  mdb_err outErr = 0;
+  nsIMdbTable* outTable = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( mTableRowCursor_Table )
+      outTable = mTableRowCursor_Table->AcquireTableHandle(ev);
+    
+    outErr = ev->AsErr();
+  }
+  if ( acqTable )
+    *acqTable = outTable;
+  return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin oid iteration methods -----
+NS_IMETHODIMP
+morkTableRowCursor::NextRowOid( // get row id of next row in the table
+  nsIMdbEnv* mev, // context
+  mdbOid* outOid, // out row oid
+  mdb_pos* outRowPos)
+{
+  mdb_err outErr = 0;
+  mork_pos pos = -1;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( outOid )
+    {
+      pos = NextRowOid(ev, outOid);
+    }
+    else
+      ev->NilPointerError();
+    outErr = ev->AsErr();
+  }
+  if ( outRowPos )
+    *outRowPos = pos;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTableRowCursor::PrevRowOid( // get row id of previous row in the table
+  nsIMdbEnv* mev, // context
+  mdbOid* outOid, // out row oid
+  mdb_pos* outRowPos)
+{
+  mdb_err outErr = 0;
+  mork_pos pos = -1;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    if ( outOid )
+    {
+      pos = PrevRowOid(ev, outOid);
+    }
+    else
+      ev->NilPointerError();
+    outErr = ev->AsErr();
+  }
+  if ( outRowPos )
+    *outRowPos = pos;
+  return outErr;
+}
+// } ----- end oid iteration methods -----
+
+// { ----- begin row iteration methods -----
+NS_IMETHODIMP
+morkTableRowCursor::NextRow( // get row cells from table for cells already in row
+  nsIMdbEnv* mev, // context
+  nsIMdbRow** acqRow, // acquire next row in table
+  mdb_pos* outRowPos)
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+      
+    mdbOid oid; // place to put oid we intend to ignore
+    morkRow* row = NextRow(ev, &oid, outRowPos);
+    if ( row )
+    {
+      morkStore* store = row->GetRowSpaceStore(ev);
+      if ( store )
+        outRow = row->AcquireRowHandle(ev, store);
+    }
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkTableRowCursor::PrevRow( // get row cells from table for cells already in row
+  nsIMdbEnv* mev, // context
+  nsIMdbRow** acqRow, // acquire previous row in table
+  mdb_pos* outRowPos)
+{
+  mdb_err outErr = 0;
+  nsIMdbRow* outRow = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+      
+    mdbOid oid; // place to put oid we intend to ignore
+    morkRow* row = PrevRow(ev, &oid, outRowPos);
+    if ( row )
+    {
+      morkStore* store = row->GetRowSpaceStore(ev);
+      if ( store )
+        outRow = row->AcquireRowHandle(ev, store);
+    }
+    outErr = ev->AsErr();
+  }
+  if ( acqRow )
+    *acqRow = outRow;
+  return outErr;
+}
+
+// } ----- end row iteration methods -----
+
+
+// { ----- begin duplicate row removal methods -----
+NS_IMETHODIMP
+morkTableRowCursor::CanHaveDupRowMembers(nsIMdbEnv* mev, // cursor might hold dups?
+  mdb_bool* outCanHaveDups)
+{
+  mdb_err outErr = 0;
+  mdb_bool canHaveDups = mdbBool_kFalse;
+  
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    canHaveDups = CanHaveDupRowMembers(ev);
+    outErr = ev->AsErr();
+  }
+  if ( outCanHaveDups )
+    *outCanHaveDups = canHaveDups;
+  return outErr;
+}
+  
+NS_IMETHODIMP
+morkTableRowCursor::MakeUniqueCursor( // clone cursor, removing duplicate rows
+  nsIMdbEnv* mev, // context
+  nsIMdbTableRowCursor** acqCursor)    // acquire clone with no dups
+  // Note that MakeUniqueCursor() is never necessary for a cursor which was
+  // created by table method nsIMdbTable::GetTableRowCursor(), because a table
+  // never contains the same row as a member more than once.  However, a cursor
+  // created by table method nsIMdbTable::FindRowMatches() might contain the
+  // same row more than once, because the same row can generate a hit by more
+  // than one column with a matching string prefix.  Note this method can
+  // return the very same cursor instance with just an incremented refcount,
+  // when the original cursor could not contain any duplicate rows (calling
+  // CanHaveDupRowMembers() shows this case on a false return).  Otherwise
+  // this method returns a different cursor instance.  Callers should not use
+  // this MakeUniqueCursor() method lightly, because it tends to defeat the
+  // purpose of lazy programming techniques, since it can force creation of
+  // an explicit row collection in a new cursor's representation, in order to
+  // inspect the row membership and remove any duplicates; this can have big
+  // impact if a collection holds tens of thousands of rows or more, when
+  // the original cursor with dups simply referenced rows indirectly by row
+  // position ranges, without using an explicit row set representation.
+  // Callers are encouraged to use nsIMdbCursor::GetCount() to determine
+  // whether the row collection is very large (tens of thousands), and to
+  // delay calling MakeUniqueCursor() when possible, until a user interface
+  // element actually demands the creation of an explicit set representation.
+{
+  mdb_err outErr = 0;
+  nsIMdbTableRowCursor* outCursor = 0;
+  
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    AddRef();
+    outCursor = this;
+      
+    outErr = ev->AsErr();
+  }
+  if ( acqCursor )
+    *acqCursor = outCursor;
+  return outErr;
+}
+// } ----- end duplicate row removal methods -----
+
+// } ===== end nsIMdbTableRowCursor methods =====
+
+
+/*static*/ void
+morkTableRowCursor::NonTableRowCursorTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkTableRowCursor");
+}
+
+
+mdb_pos
+morkTableRowCursor::NextRowOid(morkEnv* ev, mdbOid* outOid)
+{
+  mdb_pos outPos = -1;
+  (void) this->NextRow(ev, outOid, &outPos);
+  return outPos;
+}
+
+mdb_pos
+morkTableRowCursor::PrevRowOid(morkEnv* ev, mdbOid* outOid)
+{
+  mdb_pos outPos = -1;
+  (void) this->PrevRow(ev, outOid, &outPos);
+  return outPos;
+}
+
+mork_bool
+morkTableRowCursor::CanHaveDupRowMembers(morkEnv* ev)
+{
+  return morkBool_kFalse; // false default is correct
+}
+
+mork_count
+morkTableRowCursor::GetMemberCount(morkEnv* ev)
+{
+  morkTable* table = mTableRowCursor_Table;
+  if ( table )
+    return table->mTable_RowArray.mArray_Fill;
+  else
+    return 0;
+}
+
+morkRow*
+morkTableRowCursor::PrevRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos)
+{
+  morkRow* outRow = 0;
+  mork_pos pos = -1;
+  
+  morkTable* table = mTableRowCursor_Table;
+  if ( table )
+  {
+    if ( table->IsOpenNode() )
+    {
+      morkArray* array = &table->mTable_RowArray;
+      pos = mCursor_Pos - 1;
+        
+      if ( pos >= 0 && pos < (mork_pos)(array->mArray_Fill) )
+      {
+        mCursor_Pos = pos; // update for next time
+        morkRow* row = (morkRow*) array->At(pos);
+        if ( row )
+        {
+          if ( row->IsRow() )
+          {
+            outRow = row;
+            *outOid = row->mRow_Oid;
+          }
+          else
+            row->NonRowTypeError(ev);
+        }
+        else
+          ev->NilPointerError();
+      }
+      else
+      {
+        outOid->mOid_Scope = 0;
+        outOid->mOid_Id = morkId_kMinusOne;
+      }
+    }
+    else
+      table->NonOpenNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+
+  *outPos = pos;
+  return outRow;
+}
+
+morkRow*
+morkTableRowCursor::NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos)
+{
+  morkRow* outRow = 0;
+  mork_pos pos = -1;
+  
+  morkTable* table = mTableRowCursor_Table;
+  if ( table )
+  {
+    if ( table->IsOpenNode() )
+    {
+      morkArray* array = &table->mTable_RowArray;
+      pos = mCursor_Pos;
+      if ( pos < 0 )
+        pos = 0;
+      else
+        ++pos;
+        
+      if ( pos < (mork_pos)(array->mArray_Fill) )
+      {
+        mCursor_Pos = pos; // update for next time
+        morkRow* row = (morkRow*) array->At(pos);
+        if ( row )
+        {
+          if ( row->IsRow() )
+          {
+            outRow = row;
+            *outOid = row->mRow_Oid;
+          }
+          else
+            row->NonRowTypeError(ev);
+        }
+        else
+          ev->NilPointerError();
+      }
+      else
+      {
+        outOid->mOid_Scope = 0;
+        outOid->mOid_Id = morkId_kMinusOne;
+      }
+    }
+    else
+      table->NonOpenNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+
+  *outPos = pos;
+  return outRow;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkTableRowCursor.h
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Blake Ross (blake@blakeross.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKTABLEROWCURSOR_
+#define _MORKTABLEROWCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class orkinTableRowCursor;
+#define morkDerived_kTableRowCursor  /*i*/ 0x7243 /* ascii 'rC' */
+
+class morkTableRowCursor : public morkCursor, public nsIMdbTableRowCursor { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+  // morkFactory* mObject_Factory;  // weak ref to suite factory
+
+  // mork_seed  mCursor_Seed;
+  // mork_pos   mCursor_Pos;
+  // mork_bool  mCursor_DoFailOnSeedOutOfSync;
+  // mork_u1    mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+  morkTable*  mTableRowCursor_Table; // weak ref to table
+    
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseTableRowCursor()
+  virtual ~morkTableRowCursor(); // assert that close executed earlier
+  
+public: // morkTableRowCursor construction & destruction
+  morkTableRowCursor(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos);
+  void CloseTableRowCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkTableRowCursor(const morkTableRowCursor& other);
+  morkTableRowCursor& operator=(const morkTableRowCursor& other);
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // { ----- begin attribute methods -----
+  NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount); // readonly
+  NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed);    // readonly
+  
+  NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos);   // mutable
+  NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos);
+  
+  NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail);
+  NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail);
+
+  // } ----- end attribute methods -----
+    NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable);
+  // } ----- end attribute methods -----
+
+  // { ----- begin duplicate row removal methods -----
+  NS_IMETHOD CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups?
+    mdb_bool* outCanHaveDups);
+    
+  NS_IMETHOD MakeUniqueCursor( // clone cursor, removing duplicate rows
+    nsIMdbEnv* ev, // context
+    nsIMdbTableRowCursor** acqCursor);    // acquire clone with no dups
+  // } ----- end duplicate row removal methods -----
+
+  // { ----- begin oid iteration methods -----
+  NS_IMETHOD NextRowOid( // get row id of next row in the table
+    nsIMdbEnv* ev, // context
+    mdbOid* outOid, // out row oid
+    mdb_pos* outRowPos);    // zero-based position of the row in table
+  NS_IMETHOD PrevRowOid( // get row id of previous row in the table
+    nsIMdbEnv* ev, // context
+    mdbOid* outOid, // out row oid
+    mdb_pos* outRowPos);    // zero-based position of the row in table
+  // } ----- end oid iteration methods -----
+
+  // { ----- begin row iteration methods -----
+  NS_IMETHOD NextRow( // get row cells from table for cells already in row
+    nsIMdbEnv* ev, // context
+    nsIMdbRow** acqRow, // acquire next row in table
+    mdb_pos* outRowPos);    // zero-based position of the row in table
+  NS_IMETHOD PrevRow( // get row cells from table for cells already in row
+    nsIMdbEnv* ev, // context
+    nsIMdbRow** acqRow, // acquire previous row in table
+    mdb_pos* outRowPos);    // zero-based position of the row in table
+  // } ----- end row iteration methods -----
+
+
+public: // dynamic type identification
+  mork_bool IsTableRowCursor() const
+  { return IsNode() && mNode_Derived == morkDerived_kTableRowCursor; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonTableRowCursorTypeError(morkEnv* ev);
+
+public: // oid only iteration
+  mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid);
+  mdb_pos PrevRowOid(morkEnv* ev, mdbOid* outOid);
+
+public: // other table row cursor methods
+
+  virtual mork_bool CanHaveDupRowMembers(morkEnv* ev);
+  virtual mork_count GetMemberCount(morkEnv* ev);
+
+  virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos);
+  virtual morkRow* PrevRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakTableRowCursor(morkTableRowCursor* me,
+    morkEnv* ev, morkTableRowCursor** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongTableRowCursor(morkTableRowCursor* me,
+    morkEnv* ev, morkTableRowCursor** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTABLEROWCURSOR_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkThumb.cpp
@@ -0,0 +1,560 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKTHUMB_
+#include "morkThumb.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+// #ifndef _MORKFILE_
+// #include "morkFile.h"
+// #endif
+
+#ifndef _MORKWRITER_
+#include "morkWriter.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+#ifndef _MORKBUILDER_
+#include "morkBuilder.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkThumb::CloseMorkNode(morkEnv* ev) // CloseThumb() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseThumb(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkThumb::~morkThumb() // assert CloseThumb() executed earlier
+{
+  CloseMorkNode(mMorkEnv);
+  MORK_ASSERT(mThumb_Magic==0);
+  MORK_ASSERT(mThumb_Store==0);
+  MORK_ASSERT(mThumb_File==0);
+}
+
+/*public non-poly*/
+morkThumb::morkThumb(morkEnv* ev,
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+  nsIMdbHeap* ioSlotHeap, mork_magic inMagic)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mThumb_Magic( 0 )
+, mThumb_Total( 0 )
+, mThumb_Current( 0 )
+
+, mThumb_Done( morkBool_kFalse )
+, mThumb_Broken( morkBool_kFalse )
+, mThumb_Seed( 0 )
+
+, mThumb_Store( 0 )
+, mThumb_File( 0 )
+, mThumb_Writer( 0 )
+, mThumb_Builder( 0 )
+, mThumb_SourcePort( 0 )
+
+, mThumb_DoCollect( morkBool_kFalse )
+{
+  if ( ev->Good() )
+  {
+    if ( ioSlotHeap )
+    {
+      mThumb_Magic = inMagic;
+      mNode_Derived = morkDerived_kThumb;
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(morkThumb, morkObject, nsIMdbThumb)
+
+/*public non-poly*/ void
+morkThumb::CloseThumb(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      mThumb_Magic = 0;
+      if ( mThumb_Builder && mThumb_Store )
+        mThumb_Store->ForgetBuilder(ev);
+      morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mThumb_Builder);
+      
+      morkWriter::SlotStrongWriter((morkWriter*) 0, ev, &mThumb_Writer);
+      nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mThumb_File);
+      morkStore::SlotStrongStore((morkStore*) 0, ev, &mThumb_Store);
+      morkStore::SlotStrongPort((morkPort*) 0, ev, &mThumb_SourcePort);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+// { ===== begin nsIMdbThumb methods =====
+NS_IMETHODIMP
+morkThumb::GetProgress(nsIMdbEnv* mev, mdb_count* outTotal,
+  mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    GetProgress(ev, outTotal, outCurrent, outDone, outBroken);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkThumb::DoMore(nsIMdbEnv* mev, mdb_count* outTotal,
+  mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    DoMore(ev, outTotal, outCurrent, outDone, outBroken);
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+
+NS_IMETHODIMP
+morkThumb::CancelAndBreakThumb(nsIMdbEnv* mev)
+{
+  mdb_err outErr = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    mThumb_Done = morkBool_kTrue;
+    mThumb_Broken = morkBool_kTrue;
+    CloseMorkNode(ev); // should I close this here?
+    outErr = ev->AsErr();
+  }
+  return outErr;
+}
+// } ===== end nsIMdbThumb methods =====
+
+/*static*/ void morkThumb::NonThumbTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkThumb");
+}
+
+/*static*/ void morkThumb::UnsupportedThumbMagicError(morkEnv* ev)
+{
+  ev->NewError("unsupported mThumb_Magic");
+}
+
+/*static*/ void morkThumb::NilThumbStoreError(morkEnv* ev)
+{
+  ev->NewError("nil mThumb_Store");
+}
+
+/*static*/ void morkThumb::NilThumbFileError(morkEnv* ev)
+{
+  ev->NewError("nil mThumb_File");
+}
+
+/*static*/ void morkThumb::NilThumbWriterError(morkEnv* ev)
+{
+  ev->NewError("nil mThumb_Writer");
+}
+
+/*static*/ void morkThumb::NilThumbBuilderError(morkEnv* ev)
+{
+  ev->NewError("nil mThumb_Builder");
+}
+
+/*static*/ void morkThumb::NilThumbSourcePortError(morkEnv* ev)
+{
+  ev->NewError("nil mThumb_SourcePort");
+}
+
+/*static*/ morkThumb*
+morkThumb::Make_OpenFileStore(morkEnv* ev, nsIMdbHeap* ioHeap, 
+  morkStore* ioStore)
+{
+  morkThumb* outThumb = 0;
+  if ( ioHeap && ioStore )
+  {
+    nsIMdbFile* file = ioStore->mStore_File;
+    if ( file )
+    {
+      mork_pos fileEof = 0;
+      file->Eof(ev->AsMdbEnv(), &fileEof);
+      if ( ev->Good() )
+      {
+        outThumb = new(*ioHeap, ev)
+          morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap,
+            morkThumb_kMagic_OpenFileStore);
+            
+        if ( outThumb )
+        {
+          morkBuilder* builder = ioStore->LazyGetBuilder(ev);
+          if ( builder )
+          {
+            outThumb->mThumb_Total = (mork_count) fileEof;
+            morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store);
+            morkBuilder::SlotStrongBuilder(builder, ev,
+              &outThumb->mThumb_Builder);
+          }
+        }
+      }
+    }
+    else
+      ioStore->NilStoreFileError(ev);
+  }
+  else
+    ev->NilPointerError();
+    
+  return outThumb;
+}
+
+
+/*static*/ morkThumb*
+morkThumb::Make_LargeCommit(morkEnv* ev, 
+  nsIMdbHeap* ioHeap, morkStore* ioStore)
+{
+  morkThumb* outThumb = 0;
+  if ( ioHeap && ioStore )
+  {
+    nsIMdbFile* file = ioStore->mStore_File;
+    if ( file )
+    {
+      outThumb = new(*ioHeap, ev)
+        morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap,
+          morkThumb_kMagic_LargeCommit);
+          
+      if ( outThumb )
+      {
+        morkWriter* writer = new(*ioHeap, ev)
+          morkWriter(ev, morkUsage::kHeap, ioHeap, ioStore, file, ioHeap);
+        if ( writer )
+        {
+          writer->mWriter_CommitGroupIdentity =
+            ++ioStore->mStore_CommitGroupIdentity;
+          writer->mWriter_NeedDirtyAll = morkBool_kFalse;
+          outThumb->mThumb_DoCollect = morkBool_kFalse;
+          morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store);
+          
+          nsIMdbFile_SlotStrongFile(file, ev, &outThumb->mThumb_File);
+          
+          outThumb->mThumb_Writer = writer; // pass writer ownership to thumb
+        }
+      }
+    }
+    else
+      ioStore->NilStoreFileError(ev);
+  }
+  else
+    ev->NilPointerError();
+    
+  return outThumb;
+}
+
+/*static*/ morkThumb*
+morkThumb::Make_CompressCommit(morkEnv* ev, 
+  nsIMdbHeap* ioHeap, morkStore* ioStore, mork_bool inDoCollect)
+{
+  morkThumb* outThumb = 0;
+  if ( ioHeap && ioStore )
+  {
+    nsIMdbFile* file = ioStore->mStore_File;
+    if ( file )
+    {
+      outThumb = new(*ioHeap, ev)
+        morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap,
+          morkThumb_kMagic_CompressCommit);
+          
+      if ( outThumb )
+      {
+        morkWriter* writer = new(*ioHeap, ev)
+          morkWriter(ev, morkUsage::kHeap, ioHeap, ioStore, file, ioHeap);
+        if ( writer )
+        {
+          writer->mWriter_NeedDirtyAll = morkBool_kTrue;
+          outThumb->mThumb_DoCollect = inDoCollect;
+          morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store);
+          nsIMdbFile_SlotStrongFile(file, ev, &outThumb->mThumb_File);
+          outThumb->mThumb_Writer = writer; // pass writer ownership to thumb
+          
+          // cope with fact that parsed transaction groups are going away:
+          ioStore->mStore_FirstCommitGroupPos = 0;
+          ioStore->mStore_SecondCommitGroupPos = 0;
+        }
+      }
+    }
+    else
+      ioStore->NilStoreFileError(ev);
+  }
+  else
+    ev->NilPointerError();
+    
+  return outThumb;
+}
+
+// { ===== begin non-poly methods imitating nsIMdbThumb =====
+void morkThumb::GetProgress(morkEnv* ev, mdb_count* outTotal,
+  mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken)
+{
+  MORK_USED_1(ev);
+  if ( outTotal )
+    *outTotal = mThumb_Total;
+  if ( outCurrent )
+    *outCurrent = mThumb_Current;
+  if ( outDone )
+    *outDone = mThumb_Done;
+  if ( outBroken )
+    *outBroken = mThumb_Broken;
+}
+
+void morkThumb::DoMore(morkEnv* ev, mdb_count* outTotal,
+  mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken)
+{
+  if ( !mThumb_Done && !mThumb_Broken )
+  {
+    switch ( mThumb_Magic )
+    {
+      case morkThumb_kMagic_OpenFilePort: // 1 /* factory method */
+        this->DoMore_OpenFilePort(ev); break;
+
+      case morkThumb_kMagic_OpenFileStore: // 2 /* factory method */
+        this->DoMore_OpenFileStore(ev); break;
+
+      case morkThumb_kMagic_ExportToFormat: // 3 /* port method */
+        this->DoMore_ExportToFormat(ev); break;
+
+      case morkThumb_kMagic_ImportContent: // 4 /* store method */
+        this->DoMore_ImportContent(ev); break;
+
+      case morkThumb_kMagic_LargeCommit: // 5 /* store method */
+        this->DoMore_LargeCommit(ev); break;
+
+      case morkThumb_kMagic_SessionCommit: // 6 /* store method */
+        this->DoMore_SessionCommit(ev); break;
+
+      case morkThumb_kMagic_CompressCommit: // 7 /* store method */
+        this->DoMore_CompressCommit(ev); break;
+
+      case morkThumb_kMagic_SearchManyColumns: // 8 /* table method */
+        this->DoMore_SearchManyColumns(ev); break;
+
+      case morkThumb_kMagic_NewSortColumn: // 9 /* table metho) */
+        this->DoMore_NewSortColumn(ev); break;
+
+      case morkThumb_kMagic_NewSortColumnWithCompare: // 10 /* table method */
+        this->DoMore_NewSortColumnWithCompare(ev); break;
+
+      case morkThumb_kMagic_CloneSortColumn: // 11 /* table method */
+        this->DoMore_CloneSortColumn(ev); break;
+
+      case morkThumb_kMagic_AddIndex: // 12 /* table method */
+        this->DoMore_AddIndex(ev); break;
+
+      case morkThumb_kMagic_CutIndex: // 13 /* table method */
+        this->DoMore_CutIndex(ev); break;
+
+      default:
+        this->UnsupportedThumbMagicError(ev);
+        break;
+    }
+  }
+  if ( outTotal )
+    *outTotal = mThumb_Total;
+  if ( outCurrent )
+    *outCurrent = mThumb_Current;
+  if ( outDone )
+    *outDone = mThumb_Done;
+  if ( outBroken )
+    *outBroken = mThumb_Broken;
+}
+
+void morkThumb::CancelAndBreakThumb(morkEnv* ev)
+{
+  MORK_USED_1(ev);
+  mThumb_Broken = morkBool_kTrue;
+}
+
+// } ===== end non-poly methods imitating nsIMdbThumb =====
+
+morkStore*
+morkThumb::ThumbToOpenStore(morkEnv* ev)
+// for orkinFactory::ThumbToOpenStore() after OpenFileStore()
+{
+  MORK_USED_1(ev);
+  return mThumb_Store;
+}
+
+void morkThumb::DoMore_OpenFilePort(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_OpenFileStore(morkEnv* ev)
+{
+  morkBuilder* builder = mThumb_Builder;
+  if ( builder )
+  {
+    mork_pos pos = 0;
+    builder->ParseMore(ev, &pos, &mThumb_Done, &mThumb_Broken);
+    // mThumb_Total = builder->mBuilder_TotalCount;
+    // mThumb_Current = builder->mBuilder_DoneCount;
+    mThumb_Current = (mork_count) pos;
+  }
+  else
+  {
+    this->NilThumbBuilderError(ev);
+    mThumb_Broken = morkBool_kTrue;
+    mThumb_Done = morkBool_kTrue;
+  }
+}
+
+void morkThumb::DoMore_ExportToFormat(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_ImportContent(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_LargeCommit(morkEnv* ev)
+{
+  this->DoMore_Commit(ev);
+}
+
+void morkThumb::DoMore_SessionCommit(morkEnv* ev)
+{
+  this->DoMore_Commit(ev);
+}
+
+void morkThumb::DoMore_Commit(morkEnv* ev)
+{
+  morkWriter* writer = mThumb_Writer;
+  if ( writer )
+  {
+    writer->WriteMore(ev);
+    mThumb_Total = writer->mWriter_TotalCount;
+    mThumb_Current = writer->mWriter_DoneCount;
+    mThumb_Done = ( ev->Bad() || writer->IsWritingDone() );
+    mThumb_Broken = ev->Bad();
+  }
+  else
+  {
+    this->NilThumbWriterError(ev);
+    mThumb_Broken = morkBool_kTrue;
+    mThumb_Done = morkBool_kTrue;
+  }
+}
+
+void morkThumb::DoMore_CompressCommit(morkEnv* ev)
+{
+  this->DoMore_Commit(ev);
+}
+
+void morkThumb::DoMore_SearchManyColumns(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_NewSortColumn(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_NewSortColumnWithCompare(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_CloneSortColumn(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_AddIndex(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_CutIndex(morkEnv* ev)
+{
+  this->UnsupportedThumbMagicError(ev);
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkThumb.h
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKTHUMB_
+#define _MORKTHUMB_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+#define morkThumb_kMagic_OpenFilePort               1 /* factory method */
+#define morkThumb_kMagic_OpenFileStore              2 /* factory method */
+#define morkThumb_kMagic_ExportToFormat             3 /* port method */
+#define morkThumb_kMagic_ImportContent              4 /* store method */
+#define morkThumb_kMagic_LargeCommit                5 /* store method */
+#define morkThumb_kMagic_SessionCommit              6 /* store method */
+#define morkThumb_kMagic_CompressCommit             7 /* store method */
+#define morkThumb_kMagic_SearchManyColumns          8 /* table method */
+#define morkThumb_kMagic_NewSortColumn              9 /* table metho) */
+#define morkThumb_kMagic_NewSortColumnWithCompare  10 /* table method */
+#define morkThumb_kMagic_CloneSortColumn           11 /* table method */
+#define morkThumb_kMagic_AddIndex                  12 /* table method */
+#define morkThumb_kMagic_CutIndex                  13 /* table method */
+
+#define morkDerived_kThumb     /*i*/ 0x5468 /* ascii 'Th' */
+
+/*| morkThumb: 
+|*/
+class morkThumb : public morkObject, public nsIMdbThumb {
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*    mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+  // mork_color   mBead_Color;   // ID for this bead
+  // morkHandle*  mObject_Handle;  // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+  NS_DECL_ISUPPORTS_INHERITED
+
+// { ===== begin nsIMdbThumb methods =====
+  NS_IMETHOD GetProgress(nsIMdbEnv* ev, mdb_count* outTotal,
+    mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken);
+  
+  NS_IMETHOD DoMore(nsIMdbEnv* ev, mdb_count* outTotal,
+    mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken);
+  
+  NS_IMETHOD CancelAndBreakThumb(nsIMdbEnv* ev);
+// } ===== end nsIMdbThumb methods =====
+
+  // might as well include all the return values here:
+  
+  mork_magic   mThumb_Magic;   // magic sig different in each thumb type
+  mork_count   mThumb_Total;
+  mork_count   mThumb_Current;
+
+  mork_bool    mThumb_Done;
+  mork_bool    mThumb_Broken;
+  mork_u2      mThumb_Seed;  // optional seed for u4 alignment padding
+  
+  morkStore*   mThumb_Store; // weak ref to created store
+  nsIMdbFile*  mThumb_File;  // strong ref to file (store, import, export)
+  morkWriter*  mThumb_Writer;  // strong ref to writer (for commit)
+  morkBuilder* mThumb_Builder;  // strong ref to builder (for store open)
+  morkPort*    mThumb_SourcePort;  // strong ref to port for import
+  
+  mork_bool    mThumb_DoCollect; // influence whether a collect happens
+  mork_bool    mThumb_Pad[ 3 ]; // padding for u4 alignment
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseThumb() only if open
+  virtual ~morkThumb(); // assert that CloseThumb() executed earlier
+  
+public: // morkThumb construction & destruction
+  morkThumb(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_magic inMagic);
+  void CloseThumb(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkThumb(const morkThumb& other);
+  morkThumb& operator=(const morkThumb& other);
+
+public: // dynamic type identification
+  mork_bool IsThumb() const
+  { return IsNode() && mNode_Derived == morkDerived_kThumb; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonThumbTypeError(morkEnv* ev);
+  static void UnsupportedThumbMagicError(morkEnv* ev);
+
+  static void NilThumbStoreError(morkEnv* ev);
+  static void NilThumbFileError(morkEnv* ev);
+  static void NilThumbWriterError(morkEnv* ev);
+  static void NilThumbBuilderError(morkEnv* ev);
+  static void NilThumbSourcePortError(morkEnv* ev);
+
+public: // 'do more' methods
+
+  void DoMore_OpenFilePort(morkEnv* ev);
+  void DoMore_OpenFileStore(morkEnv* ev);
+  void DoMore_ExportToFormat(morkEnv* ev);
+  void DoMore_ImportContent(morkEnv* ev);
+  void DoMore_LargeCommit(morkEnv* ev);
+  void DoMore_SessionCommit(morkEnv* ev);
+  void DoMore_CompressCommit(morkEnv* ev);
+  void DoMore_Commit(morkEnv* ev);
+  void DoMore_SearchManyColumns(morkEnv* ev);
+  void DoMore_NewSortColumn(morkEnv* ev);
+  void DoMore_NewSortColumnWithCompare(morkEnv* ev);
+  void DoMore_CloneSortColumn(morkEnv* ev);
+  void DoMore_AddIndex(morkEnv* ev);
+  void DoMore_CutIndex(morkEnv* ev);
+
+public: // other thumb methods
+
+  morkStore* ThumbToOpenStore(morkEnv* ev);
+  // for orkinFactory::ThumbToOpenStore() after OpenFileStore()
+
+public: // assorted thumb constructors
+
+  static morkThumb* Make_OpenFileStore(morkEnv* ev, 
+    nsIMdbHeap* ioHeap, morkStore* ioStore);
+
+  static morkThumb* Make_CompressCommit(morkEnv* ev, 
+    nsIMdbHeap* ioHeap, morkStore* ioStore, mork_bool inDoCollect);
+
+  static morkThumb* Make_LargeCommit(morkEnv* ev, 
+    nsIMdbHeap* ioHeap, morkStore* ioStore);
+
+// { ===== begin non-poly methods imitating nsIMdbThumb =====
+  void GetProgress(morkEnv* ev, mdb_count* outTotal,
+    mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken);
+  
+  void DoMore(morkEnv* ev, mdb_count* outTotal,
+    mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken);
+  
+  void CancelAndBreakThumb(morkEnv* ev);
+// } ===== end non-poly methods imitating nsIMdbThumb =====
+
+
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakThumb(morkThumb* me,
+    morkEnv* ev, morkThumb** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongThumb(morkThumb* me,
+    morkEnv* ev, morkThumb** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTHUMB_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkUniqRowCursor.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKUNIQROWCURSOR_
+#define _MORKUNIQROWCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class orkinTableRowCursor;
+// #define morkDerived_kUniqRowCursor  /*i*/ 0x7352 /* ascii 'sR' */
+
+class morkUniqRowCursor : public morkTableRowCursor { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+  // morkFactory* mObject_Factory;  // weak ref to suite factory
+
+  // mork_seed  mCursor_Seed;
+  // mork_pos   mCursor_Pos;
+  // mork_bool  mCursor_DoFailOnSeedOutOfSync;
+  // mork_u1    mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+  // morkTable*  mTableRowCursor_Table; // weak ref to table
+
+public: // state is public because the entire Mork system is private
+    
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseUniqRowCursor()
+  virtual ~morkUniqRowCursor(); // assert that close executed earlier
+  
+public: // morkUniqRowCursor construction & destruction
+  morkUniqRowCursor(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos);
+  void CloseUniqRowCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkUniqRowCursor(const morkUniqRowCursor& other);
+  morkUniqRowCursor& operator=(const morkUniqRowCursor& other);
+
+public: // dynamic type identification
+  // mork_bool IsUniqRowCursor() const
+  // { return IsNode() && mNode_Derived == morkDerived_kUniqRowCursor; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonUniqRowCursorTypeError(morkEnv* ev);
+
+public: // other search row cursor methods
+
+  virtual mork_bool CanHaveDupRowMembers(morkEnv* ev);
+  virtual mork_count GetMemberCount(morkEnv* ev);
+
+  virtual orkinTableRowCursor* AcquireUniqueRowCursorHandle(morkEnv* ev);
+  
+  // virtual mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid);
+  virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakUniqRowCursor(morkUniqRowCursor* me,
+    morkEnv* ev, morkUniqRowCursor** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongUniqRowCursor(morkUniqRowCursor* me,
+    morkEnv* ev, morkUniqRowCursor** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKUNIQROWCURSOR_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkWriter.cpp
@@ -0,0 +1,2243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkWriter.h"
+#endif
+
+// #ifndef _MORKFILE_
+// #include "morkFile.h"
+// #endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKCH_
+#include "morkCh.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkWriter::CloseMorkNode(morkEnv* ev) // CloseTable() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseWriter(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkWriter::~morkWriter() // assert CloseTable() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+  MORK_ASSERT(mWriter_Store==0);
+}
+
+/*public non-poly*/
+morkWriter::morkWriter(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbFile* ioFile,
+    nsIMdbHeap* ioSlotHeap)
+: morkNode(ev, inUsage, ioHeap)
+, mWriter_Store( 0 )
+, mWriter_File( 0 )
+, mWriter_Bud( 0 )
+, mWriter_Stream( 0 )
+, mWriter_SlotHeap( 0 )
+
+, mWriter_CommitGroupIdentity( 0 ) // see mStore_CommitGroupIdentity
+, mWriter_GroupBufFill( 0 )
+
+, mWriter_TotalCount( morkWriter_kCountNumberOfPhases )
+, mWriter_DoneCount( 0 )
+
+, mWriter_LineSize( 0 )
+, mWriter_MaxIndent( morkWriter_kMaxIndent )
+, mWriter_MaxLine( morkWriter_kMaxLine )
+  
+, mWriter_TableForm( 0 )
+, mWriter_TableAtomScope( 'v' )
+, mWriter_TableRowScope( 0 )
+, mWriter_TableKind( 0 )
+  
+, mWriter_RowForm( 0 )
+, mWriter_RowAtomScope( 0 )
+, mWriter_RowScope( 0 )
+  
+, mWriter_DictForm( 0 )
+, mWriter_DictAtomScope( 'v' )
+
+, mWriter_NeedDirtyAll( morkBool_kFalse )
+, mWriter_Incremental( morkBool_kTrue ) // opposite of mWriter_NeedDirtyAll
+, mWriter_DidStartDict( morkBool_kFalse )
+, mWriter_DidEndDict( morkBool_kTrue )
+
+, mWriter_SuppressDirtyRowNewline( morkBool_kFalse )
+, mWriter_DidStartGroup( morkBool_kFalse )
+, mWriter_DidEndGroup( morkBool_kTrue )
+, mWriter_Phase( morkWriter_kPhaseNothingDone )
+
+, mWriter_BeVerbose( ev->mEnv_BeVerbose )
+
+, mWriter_TableRowArrayPos( 0 )
+
+// empty constructors for map iterators:
+, mWriter_StoreAtomSpacesIter( )
+, mWriter_AtomSpaceAtomAidsIter( )
+  
+, mWriter_StoreRowSpacesIter( )
+, mWriter_RowSpaceTablesIter( )
+, mWriter_RowSpaceRowsIter( )
+{
+  mWriter_GroupBuf[ 0 ] = 0;
+
+  mWriter_SafeNameBuf[ 0 ] = 0;
+  mWriter_SafeNameBuf[ morkWriter_kMaxColumnNameSize * 2 ] = 0;
+  mWriter_ColNameBuf[ 0 ] = 0;
+  mWriter_ColNameBuf[ morkWriter_kMaxColumnNameSize ] = 0;
+  
+  mdbYarn* y = &mWriter_ColYarn;
+  y->mYarn_Buf = mWriter_ColNameBuf; // where to put col bytes
+  y->mYarn_Fill = 0; // set later by writer
+  y->mYarn_Size = morkWriter_kMaxColumnNameSize; // our buf size
+  y->mYarn_More = 0; // set later by writer
+  y->mYarn_Form = 0; // set later by writer
+  y->mYarn_Grow = 0; // do not allow buffer growth
+  
+  y = &mWriter_SafeYarn;
+  y->mYarn_Buf = mWriter_SafeNameBuf; // where to put col bytes
+  y->mYarn_Fill = 0; // set later by writer
+  y->mYarn_Size = morkWriter_kMaxColumnNameSize * 2; // our buf size
+  y->mYarn_More = 0; // set later by writer
+  y->mYarn_Form = 0; // set later by writer
+  y->mYarn_Grow = 0; // do not allow buffer growth
+  
+  if ( ev->Good() )
+  {
+    if ( ioSlotHeap && ioFile && ioStore )
+    {
+      morkStore::SlotWeakStore(ioStore, ev, &mWriter_Store);
+      nsIMdbFile_SlotStrongFile(ioFile, ev, &mWriter_File);
+      nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mWriter_SlotHeap);
+      if ( ev->Good() )
+      {
+        mNode_Derived = morkDerived_kWriter;
+      }
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+
+void
+morkWriter::MakeWriterStream(morkEnv* ev) // give writer a suitable stream
+{
+  mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites
+  
+  if ( !mWriter_Stream && ev->Good() )
+  {
+    if ( mWriter_File )
+    {
+      morkStream* stream = 0;
+      mork_bool frozen = morkBool_kFalse; // need to modify
+      nsIMdbHeap* heap = mWriter_SlotHeap;
+    
+      if ( mWriter_Incremental )
+      {
+        stream = new(*heap, ev)
+          morkStream(ev, morkUsage::kHeap, heap, mWriter_File,
+            morkWriter_kStreamBufSize, frozen);
+      }
+      else // compress commit
+      {
+        nsIMdbFile* bud = 0;
+        mWriter_File->AcquireBud(ev->AsMdbEnv(), heap, &bud);
+        if ( bud )
+        {
+          if ( ev->Good() )
+          {
+            mWriter_Bud = bud;
+            stream = new(*heap, ev)
+              morkStream(ev, morkUsage::kHeap, heap, bud,
+                morkWriter_kStreamBufSize, frozen);
+          }
+          else
+            bud->Release();
+        }
+      }
+        
+      if ( stream )
+      {
+        if ( ev->Good() )
+          mWriter_Stream = stream;
+        else
+          stream->CutStrongRef(ev->AsMdbEnv());
+      }
+    }
+    else
+      this->NilWriterFileError(ev);
+  }
+}
+
+/*public non-poly*/ void
+morkWriter::CloseWriter(morkEnv* ev) // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      morkStore::SlotWeakStore((morkStore*) 0, ev, &mWriter_Store);
+      nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_File);
+      nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_Bud);
+      morkStream::SlotStrongStream((morkStream*) 0, ev, &mWriter_Stream);
+      nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mWriter_SlotHeap);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkWriter::NonWriterTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkWriter");
+}
+
+/*static*/ void
+morkWriter::NilWriterStoreError(morkEnv* ev)
+{
+  ev->NewError("nil mWriter_Store");
+}
+
+/*static*/ void
+morkWriter::NilWriterBudError(morkEnv* ev)
+{
+  ev->NewError("nil mWriter_Bud");
+}
+
+/*static*/ void
+morkWriter::NilWriterFileError(morkEnv* ev)
+{
+  ev->NewError("nil mWriter_File");
+}
+
+/*static*/ void
+morkWriter::NilWriterStreamError(morkEnv* ev)
+{
+  ev->NewError("nil mWriter_Stream");
+}
+
+/*static*/ void
+morkWriter::UnsupportedPhaseError(morkEnv* ev)
+{
+  ev->NewError("unsupported mWriter_Phase");
+}
+
+mork_bool
+morkWriter::WriteMore(morkEnv* ev) // call until IsWritingDone() is true
+{
+  if ( this->IsOpenNode() )
+  {
+    if ( this->IsWriter() )
+    {
+      if ( !mWriter_Stream )
+        this->MakeWriterStream(ev);
+        
+      if ( mWriter_Stream )
+      {
+        if ( ev->Bad() )
+        {
+          ev->NewWarning("writing stops on error");
+          mWriter_Phase = morkWriter_kPhaseWritingDone;
+        }
+        switch( mWriter_Phase )
+        {
+          case morkWriter_kPhaseNothingDone:
+            OnNothingDone(ev); break;
+          
+          case morkWriter_kPhaseDirtyAllDone:
+            OnDirtyAllDone(ev); break;
+          
+          case morkWriter_kPhasePutHeaderDone:
+            OnPutHeaderDone(ev); break;
+          
+          case morkWriter_kPhaseRenumberAllDone:
+            OnRenumberAllDone(ev); break;
+          
+          case morkWriter_kPhaseStoreAtomSpaces:
+            OnStoreAtomSpaces(ev); break;
+          
+          case morkWriter_kPhaseAtomSpaceAtomAids:
+            OnAtomSpaceAtomAids(ev); break;
+          
+          case morkWriter_kPhaseStoreRowSpacesTables:
+            OnStoreRowSpacesTables(ev); break;
+          
+          case morkWriter_kPhaseRowSpaceTables:
+            OnRowSpaceTables(ev); break;
+          
+          case morkWriter_kPhaseTableRowArray:
+            OnTableRowArray(ev); break;
+          
+          case morkWriter_kPhaseStoreRowSpacesRows:
+            OnStoreRowSpacesRows(ev); break;
+          
+          case morkWriter_kPhaseRowSpaceRows:
+            OnRowSpaceRows(ev); break;
+          
+          case morkWriter_kPhaseContentDone:
+            OnContentDone(ev); break;
+          
+          case morkWriter_kPhaseWritingDone:
+            OnWritingDone(ev); break;
+          
+          default:
+            this->UnsupportedPhaseError(ev);
+        }
+      }
+      else
+        this->NilWriterStreamError(ev);
+    }
+    else
+      this->NonWriterTypeError(ev);
+  }
+  else
+    this->NonOpenNodeError(ev);
+    
+  return ev->Good();
+}
+
+static const char morkWriter_kHexDigits[] = "0123456789ABCDEF";
+
+mork_size
+morkWriter::WriteYarn(morkEnv* ev, const mdbYarn* inYarn)
+  // return number of atom bytes written on the current line (which
+  // implies that escaped line breaks will make the size value smaller
+  // than the entire yarn's size, since only part goes on a last line).
+{
+
+
+  mork_size outSize = 0;
+  mork_size lineSize = mWriter_LineSize;
+  morkStream* stream = mWriter_Stream;
+
+  const mork_u1* b = (const mork_u1*) inYarn->mYarn_Buf;
+  if ( b )
+  {
+    register int c;
+    mork_fill fill = inYarn->mYarn_Fill;
+
+    const mork_u1* end = b + fill;
+    while ( b < end && ev->Good() )
+    {
+      if ( lineSize + outSize >= mWriter_MaxLine ) // continue line?
+      {
+        stream->PutByteThenNewline(ev, '\\');
+        mWriter_LineSize = lineSize = outSize = 0;
+      }
+      
+      c = *b++; // next byte to print
+      if ( morkCh_IsValue(c) )
+      {
+        stream->Putc(ev, c);
+        ++outSize; // c
+      }
+      else if ( c == ')' || c == '$' || c == '\\' )
+      {
+        stream->Putc(ev, '\\');
+        stream->Putc(ev, c);
+        outSize += 2; // '\' c
+      }
+      else
+      {
+        outSize += 3; // '$' hex hex
+        stream->Putc(ev, '$');
+        stream->Putc(ev, morkWriter_kHexDigits[ (c >> 4) & 0x0F ]);
+        stream->Putc(ev, morkWriter_kHexDigits[ c & 0x0F ]);
+      }
+    }
+  }
+  mWriter_LineSize += outSize;
+    
+  return outSize;
+}
+
+mork_size
+morkWriter::WriteAtom(morkEnv* ev, const morkAtom* inAtom)
+  // return number of atom bytes written on the current line (which
+  // implies that escaped line breaks will make the size value smaller
+  // than the entire atom's size, since only part goes on a last line).
+{
+  mork_size outSize = 0;
+  mdbYarn yarn; // to ref content inside atom
+
+  if ( inAtom->AliasYarn(&yarn) )
+  {
+    if ( mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm )
+      this->ChangeDictForm(ev, yarn.mYarn_Form);  
+      
+    outSize = this->WriteYarn(ev, &yarn);
+    // mWriter_LineSize += stream->Write(ev, inYarn->mYarn_Buf, outSize);
+  }
+  else
+    inAtom->BadAtomKindError(ev);
+    
+  return outSize;
+}
+
+void
+morkWriter::WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace)
+{
+  morkStream* stream = mWriter_Stream;
+  nsIMdbEnv *mdbev = ev->AsMdbEnv();
+  mork_scope scope = ioSpace->SpaceScope();
+  if ( scope < 0x80 )
+  {
+    if ( mWriter_LineSize )
+      stream->PutLineBreak(ev);
+    stream->PutString(ev, "< <(a=");
+    stream->Putc(ev, (int) scope);
+    ++mWriter_LineSize;
+    stream->PutString(ev, ")> // (f=iso-8859-1)");
+    mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth);
+  }
+  else
+    ioSpace->NonAsciiSpaceScopeName(ev);
+
+  if ( ev->Good() )
+  {
+    mdbYarn yarn; // to ref content inside atom
+    char buf[ 64 ]; // buffer for staging the dict alias hex ID
+    char* idBuf = buf + 1; // where the id always starts
+    buf[ 0 ] = '('; // we always start with open paren
+    morkBookAtom* atom = 0;
+    morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter;
+    ai->InitAtomAidMapIter(ev, &ioSpace->mAtomSpace_AtomAids);
+    mork_change* c = 0;
+    
+    for ( c = ai->FirstAtom(ev, &atom); c && ev->Good();
+          c = ai->NextAtom(ev, &atom) )
+    {
+      if ( atom )
+      {
+        if ( atom->IsAtomDirty() )
+        {
+          atom->SetAtomClean(); // neutralize change
+          
+          atom->AliasYarn(&yarn);
+          mork_size size = ev->TokenAsHex(idBuf, atom->mBookAtom_Id);
+          
+          if ( yarn.mYarn_Form != mWriter_DictForm )
+            this->ChangeDictForm(ev, yarn.mYarn_Form);
+
+          mork_size pending = yarn.mYarn_Fill + size + 
+            morkWriter_kYarnEscapeSlop + 4;
+          this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
+          mork_size bytesWritten;
+          stream->Write(mdbev, buf, size+1, &bytesWritten); //  + '('
+          mWriter_LineSize += bytesWritten;
+          
+          pending -= ( size + 1 );
+          this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth);
+          stream->Putc(ev, '='); // start alias
+          ++mWriter_LineSize;
+          
+          this->WriteYarn(ev, &yarn);
+          stream->Putc(ev, ')'); // end alias
+          ++mWriter_LineSize;
+          
+          ++mWriter_DoneCount;
+        }
+      }
+      else
+        ev->NilPointerError();
+    }
+    ai->CloseMapIter(ev);
+  }
+  
+  if ( ev->Good() )
+  {
+    ioSpace->SetAtomSpaceClean();
+    // this->IndentAsNeeded(ev, 0);
+    // stream->PutByteThenNewline(ev, '>'); // end dict
+    
+    stream->Putc(ev, '>'); // end dict
+    ++mWriter_LineSize;
+  }
+}
+
+/*
+(I'm putting the text of this message in file morkWriter.cpp.)
+
+I'm making a change which should cause rows and tables to go away
+when a Mork db is compress committed, when the rows and tables
+are no longer needed.  Because this is subtle, I'm describing it
+here in case misbehavior is ever observed.  Otherwise you'll have
+almost no hope of fixing a related bug.
+
+This is done entirely in morkWriter.cpp: morkWriter::DirtyAll(),
+which currently marks all rows and tables dirty so they will be
+written in a later phase of the commit.  My change is to merely
+selectively not mark certain rows and tables dirty, when they seem
+to be superfluous.
+
+A row is no longer needed when the mRow_GcUses slot hits zero, and
+this is used by the following inline morkRow method:
+
+  mork_bool IsRowUsed() const { return mRow_GcUses != 0; }
+
+Naturally disaster ensues if mRow_GcUses is ever smaller than right.
+
+Similarly, we should drop tables when mTable_GcUses hits zero, but
+only when a table contains no row members.  We consider tables to
+self reference (and prevent collection) when they contain content.
+Again, disaster ensues if mTable_GcUses is ever smaller than right.
+
+  mork_count GetRowCount() const
+  { return mTable_RowArray.mArray_Fill; }
+
+  mork_bool IsTableUsed() const
+  { return (mTable_GcUses != 0 || this->GetRowCount() != 0); }
+
+Now let's question why the design involves filtering what gets set
+to dirty.  Why not apply a filter in the later phase when we write
+content?  Because I'm afraid of missing some subtle interaction in
+updating table and row relationships.  It seems safer to write a row
+or table when it starts out dirty, before morkWriter::DirtyAll() is
+called.  So this design calls for writing out rows and tables when
+they are still clearly used, and additionally, <i>when we have just
+been actively writing to them right before this commit</i>.
+
+Presumably if they are truly useless, they will no longer be dirtied
+in later sessions and will get collected during the next compress
+commit.  So we wait to collect them until they become all dead, and
+not just mostly dead.  (At which time you can feel free to go through
+their pockets looking for loose change.)
+*/
+
+mork_bool
+morkWriter::DirtyAll(morkEnv* ev)
+  // DirtyAll() visits every store sub-object and marks 
+  // them dirty, including every table, row, cell, and atom.  The return
+  // equals ev->Good(), to show whether any error happened.  This method is
+  // intended for use in the beginning of a "compress commit" which writes
+  // all store content, whether dirty or not.  We dirty everything first so
+  // that later iterations over content can mark things clean as they are
+  // written, and organize the process of serialization so that objects are
+  // written only at need (because of being dirty).  Note the method can 
+  // stop early when any error happens, since this will abort any commit.
+{
+  morkStore* store = mWriter_Store;
+  if ( store )
+  {
+    store->SetStoreDirty();
+    mork_change* c = 0;
+
+    if ( ev->Good() )
+    {
+      morkAtomSpaceMapIter* asi = &mWriter_StoreAtomSpacesIter;
+      asi->InitAtomSpaceMapIter(ev, &store->mStore_AtomSpaces);
+
+      mork_scope* key = 0; // ignore keys in map
+      morkAtomSpace* space = 0; // old val node in the map
+      
+      for ( c = asi->FirstAtomSpace(ev, key, &space); c && ev->Good();
+            c = asi->NextAtomSpace(ev, key, &space) )
+      {
+        if ( space )
+        {
+          if ( space->IsAtomSpace() )
+          {
+            space->SetAtomSpaceDirty();
+            morkBookAtom* atom = 0;
+            morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter;
+            ai->InitAtomAidMapIter(ev, &space->mAtomSpace_AtomAids);
+            
+            for ( c = ai->FirstAtom(ev, &atom); c && ev->Good();
+                  c = ai->NextAtom(ev, &atom) )
+            {
+              if ( atom )
+              {
+                atom->SetAtomDirty();
+                ++mWriter_TotalCount;
+              }
+              else
+                ev->NilPointerError();
+            }
+            
+            ai->CloseMapIter(ev);
+          }
+          else
+            space->NonAtomSpaceTypeError(ev);
+        }
+        else
+          ev->NilPointerError();
+      }
+    }
+    
+    if ( ev->Good() )
+    {
+      morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter;
+      rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);
+
+      mork_scope* key = 0; // ignore keys in map
+      morkRowSpace* space = 0; // old val node in the map
+      
+      for ( c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good();
+            c = rsi->NextRowSpace(ev, key, &space) )
+      {
+        if ( space )
+        {
+          if ( space->IsRowSpace() )
+          {
+            space->SetRowSpaceDirty();
+            if ( ev->Good() )
+            {
+#ifdef MORK_ENABLE_PROBE_MAPS
+              morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter;
+#else /*MORK_ENABLE_PROBE_MAPS*/
+              morkRowMapIter* ri = &mWriter_RowSpaceRowsIter;
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+              ri->InitRowMapIter(ev, &space->mRowSpace_Rows);
+
+              morkRow* row = 0; // old key row in the map
+                
+              for ( c = ri->FirstRow(ev, &row); c && ev->Good();
+                    c = ri->NextRow(ev, &row) )
+              {
+                if ( row && row->IsRow() ) // need to dirty row?
+                {
+                	if ( row->IsRowUsed() || row->IsRowDirty() )
+                	{
+	                  row->DirtyAllRowContent(ev);
+	                  ++mWriter_TotalCount;
+                	}
+                }
+                else
+                  row->NonRowTypeWarning(ev);
+              }
+              ri->CloseMapIter(ev);
+            }
+            
+            if ( ev->Good() )
+            {
+              morkTableMapIter* ti = &mWriter_RowSpaceTablesIter;
+              ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+              morkTable* table = ti->FirstTable(ev);
+                
+              for ( ; table && ev->Good(); table = ti->NextTable(ev) )
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+              mork_tid* tableKey = 0; // ignore keys in table map
+              morkTable* table = 0; // old key row in the map
+                
+              for ( c = ti->FirstTable(ev, tableKey, &table); c && ev->Good();
+                    c = ti->NextTable(ev, tableKey, &table) )
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+              {
+                if ( table && table->IsTable() ) // need to dirty table?
+                {
+                	if ( table->IsTableUsed() || table->IsTableDirty() )
+                	{
+	                  // table->DirtyAllTableContent(ev);
+	                  // only necessary to mark table itself dirty:
+	                  table->SetTableDirty();
+	                  table->SetTableRewrite();
+	                  ++mWriter_TotalCount;
+                	}
+                }
+                else
+                  table->NonTableTypeWarning(ev);
+              }
+              ti->CloseMapIter(ev);
+            }
+          }
+          else
+            space->NonRowSpaceTypeError(ev);
+        }
+        else
+          ev->NilPointerError();
+      }
+    }
+  }
+  else
+    this->NilWriterStoreError(ev);
+  
+  return ev->Good();
+}
+
+
+mork_bool
+morkWriter::OnNothingDone(morkEnv* ev)
+{
+  mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites
+  
+  if (!mWriter_Store->IsStoreDirty() && !mWriter_NeedDirtyAll)
+  {
+    mWriter_Phase = morkWriter_kPhaseWritingDone;
+    return morkBool_kTrue;
+  }
+
+  // morkStream* stream = mWriter_Stream;
+  if ( mWriter_NeedDirtyAll )
+    this->DirtyAll(ev);
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseDirtyAllDone;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+    
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::StartGroup(morkEnv* ev)
+{
+  nsIMdbEnv *mdbev = ev->AsMdbEnv();
+  morkStream* stream = mWriter_Stream;
+  mWriter_DidStartGroup = morkBool_kTrue;
+  mWriter_DidEndGroup = morkBool_kFalse;
+
+  char buf[ 64 ];
+  char* p = buf;
+  *p++ = '@';
+  *p++ = '$';
+  *p++ = '$';
+  *p++ = '{';
+  
+  mork_token groupID = mWriter_CommitGroupIdentity;
+  mork_fill idFill = ev->TokenAsHex(p, groupID);
+  mWriter_GroupBufFill = 0;
+  // ev->TokenAsHex(mWriter_GroupBuf, groupID);
+  if ( idFill < morkWriter_kGroupBufSize )
+  {
+    MORK_MEMCPY(mWriter_GroupBuf, p, idFill + 1);
+    mWriter_GroupBufFill = idFill;
+  }
+  else
+    *mWriter_GroupBuf = 0;
+    
+  p += idFill;
+  *p++ = '{';
+  *p++ = '@';
+  *p = 0;
+
+  stream->PutLineBreak(ev);
+  
+  morkStore* store = mWriter_Store;
+  if ( store ) // might need to capture commit group position?
+  {
+    mork_pos groupPos;
+    stream->Tell(mdbev, &groupPos);
+    if ( !store->mStore_FirstCommitGroupPos )
+      store->mStore_FirstCommitGroupPos = groupPos;
+    else if ( !store->mStore_SecondCommitGroupPos )
+      store->mStore_SecondCommitGroupPos = groupPos;
+  }
+  
+  mork_size bytesWritten;
+  stream->Write(mdbev, buf, idFill + 6, &bytesWritten); // '@$${' + idFill + '{@'
+  stream->PutLineBreak(ev);
+  mWriter_LineSize = 0;
+  
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::CommitGroup(morkEnv* ev)
+{
+  if ( mWriter_DidStartGroup )
+  {
+    nsIMdbEnv *mdbev = ev->AsMdbEnv();
+    mork_size bytesWritten;
+    morkStream* stream = mWriter_Stream;
+  
+    if ( mWriter_LineSize )
+      stream->PutLineBreak(ev);
+      
+    stream->Putc(ev, '@');
+    stream->Putc(ev, '$');
+    stream->Putc(ev, '$');
+    stream->Putc(ev, '}');
+    
+    mork_fill bufFill = mWriter_GroupBufFill;
+    if ( bufFill )
+      stream->Write(mdbev, mWriter_GroupBuf, bufFill, &bytesWritten);
+
+    stream->Putc(ev, '}');
+    stream->Putc(ev, '@');
+    stream->PutLineBreak(ev);
+
+    mWriter_LineSize = 0;
+  }
+
+  mWriter_DidStartGroup = morkBool_kFalse;
+  mWriter_DidEndGroup = morkBool_kTrue;
+  
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::AbortGroup(morkEnv* ev)
+{
+  if ( mWriter_DidStartGroup )
+  {
+    morkStream* stream = mWriter_Stream;
+    stream->PutLineBreak(ev);
+    stream->PutStringThenNewline(ev, "@$$}~~}@");
+    mWriter_LineSize = 0;
+  }
+  
+  mWriter_DidStartGroup = morkBool_kFalse;
+  mWriter_DidEndGroup = morkBool_kTrue;
+
+  return ev->Good();
+}
+
+
+mork_bool
+morkWriter::OnDirtyAllDone(morkEnv* ev)
+{
+  if ( ev->Good() )
+  {
+    nsIMdbEnv *mdbev = ev->AsMdbEnv();
+    morkStream* stream = mWriter_Stream;
+    mork_pos resultPos;
+    if ( mWriter_NeedDirtyAll ) // compress commit
+    {
+
+      stream->Seek(mdbev, 0, &resultPos); // beginning of stream
+      stream->PutStringThenNewline(ev, morkWriter_kFileHeader);
+      mWriter_LineSize = 0;
+    }
+    else // else mWriter_Incremental
+    {
+      mork_pos eos = stream->Length(ev); // length is end of stream
+      if ( ev->Good() )
+      {
+        stream->Seek(mdbev, eos, &resultPos); // goto end of stream
+        if ( eos < 128 ) // maybe need file header?
+        {
+          stream->PutStringThenNewline(ev, morkWriter_kFileHeader);
+          mWriter_LineSize = 0;
+        }
+        this->StartGroup(ev); // begin incremental transaction
+      }
+    }
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhasePutHeaderDone;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+    
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnPutHeaderDone(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+  
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnPutHeaderDone()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+    morkStore* store = mWriter_Store;
+    if ( store )
+      store->RenumberAllCollectableContent(ev);
+    else
+      this->NilWriterStoreError(ev);
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseRenumberAllDone;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+    
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnRenumberAllDone(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+    
+  // if ( mWriter_NeedDirtyAll )
+  //  stream->PutStringThenNewline(ev, "// OnRenumberAllDone()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseStoreAtomSpaces;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnStoreAtomSpaces(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnStoreAtomSpaces()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+  }
+  
+  if ( ev->Good() )
+  {
+    morkStore* store = mWriter_Store;
+    if ( store )
+    {
+      morkAtomSpace* space = store->LazyGetGroundColumnSpace(ev);
+      if ( space && space->IsAtomSpaceDirty() )
+      {
+        // stream->PutStringThenNewline(ev, "// ground column space dict:");
+        
+        if ( mWriter_LineSize )
+        {
+          stream->PutLineBreak(ev);
+          mWriter_LineSize = 0;
+        }
+        this->WriteAtomSpaceAsDict(ev, space);
+        space->SetAtomSpaceClean();
+      }
+    }
+    else
+      this->NilWriterStoreError(ev);
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnAtomSpaceAtomAids(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnAtomSpaceAtomAids()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+  return ev->Good();
+}
+
+void
+morkWriter::WriteAllStoreTables(morkEnv* ev)
+{
+  morkStore* store = mWriter_Store;
+  if ( store && ev->Good() )
+  {
+    morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter;
+    rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);
+
+    mork_scope* key = 0; // ignore keys in map
+    morkRowSpace* space = 0; // old val node in the map
+    mork_change* c = 0;
+    
+    for ( c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good();
+          c = rsi->NextRowSpace(ev, key, &space) )
+    {
+      if ( space )
+      {
+        if ( space->IsRowSpace() )
+        {
+          space->SetRowSpaceClean();
+          if ( ev->Good() )
+          {
+            morkTableMapIter* ti = &mWriter_RowSpaceTablesIter;
+            ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+            morkTable* table = ti->FirstTable(ev);
+              
+            for ( ; table && ev->Good(); table = ti->NextTable(ev) )
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+            mork_tid* key2 = 0; // ignore keys in table map
+            morkTable* table = 0; // old key row in the map
+              
+            for ( c = ti->FirstTable(ev, key2, &table); c && ev->Good();
+                  c = ti->NextTable(ev, key2, &table) )
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+            {
+              if ( table && table->IsTable() )
+              {
+                if ( table->IsTableDirty() )
+                {
+                  mWriter_BeVerbose =
+                    ( ev->mEnv_BeVerbose || table->IsTableVerbose() );
+                    
+                  if ( this->PutTableDict(ev, table) )
+                    this->PutTable(ev, table);
+
+                  table->SetTableClean(ev);
+                  mWriter_BeVerbose = ev->mEnv_BeVerbose;
+                }
+              }
+              else
+                table->NonTableTypeWarning(ev);
+            }
+            ti->CloseMapIter(ev);
+          }
+          if ( ev->Good() )
+          {
+            mWriter_TableRowScope = 0; // ensure no table context now
+            
+#ifdef MORK_ENABLE_PROBE_MAPS
+            morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter;
+#else /*MORK_ENABLE_PROBE_MAPS*/
+            morkRowMapIter* ri = &mWriter_RowSpaceRowsIter;
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+            ri->InitRowMapIter(ev, &space->mRowSpace_Rows);
+
+            morkRow* row = 0; // old row in the map
+              
+            for ( c = ri->FirstRow(ev, &row); c && ev->Good();
+                  c = ri->NextRow(ev, &row) )
+            {
+              if ( row && row->IsRow() )
+              {
+                // later we should also check that table use count is nonzero:
+                if ( row->IsRowDirty() ) // && row->IsRowUsed() ??
+                {
+                  mWriter_BeVerbose = ev->mEnv_BeVerbose;
+                  if ( this->PutRowDict(ev, row) )
+                  {
+                    if ( ev->Good() && mWriter_DidStartDict )
+                    {
+                      this->EndDict(ev);
+                      if ( mWriter_LineSize < 32 && ev->Good() )
+                        mWriter_SuppressDirtyRowNewline = morkBool_kTrue;
+                    }
+                      
+                    if ( ev->Good() )
+                      this->PutRow(ev, row);
+                  }
+                  mWriter_BeVerbose = ev->mEnv_BeVerbose;
+                }
+              }
+              else
+                row->NonRowTypeWarning(ev);
+            }
+            ri->CloseMapIter(ev);
+          }
+        }
+        else
+          space->NonRowSpaceTypeError(ev);
+      }
+      else
+        ev->NilPointerError();
+    }
+  }
+}
+
+mork_bool
+morkWriter::OnStoreRowSpacesTables(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnStoreRowSpacesTables()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+  }
+  
+  // later we'll break this up, but today we'll write all in one shot:
+  this->WriteAllStoreTables(ev);
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnRowSpaceTables(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnRowSpaceTables()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnTableRowArray(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnTableRowArray()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnStoreRowSpacesRows(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnStoreRowSpacesRows()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseContentDone;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnRowSpaceRows(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnRowSpaceRows()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_NeedDirtyAll ) // compress commit
+  {
+  }
+    
+  if ( ev->Good() )
+    mWriter_Phase = morkWriter_kPhaseContentDone;
+  else
+    mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnContentDone(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+
+  // if ( mWriter_NeedDirtyAll )
+  //   stream->PutStringThenNewline(ev, "// OnContentDone()");
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_Incremental )
+  {
+    if ( ev->Good() )
+      this->CommitGroup(ev);
+    else
+      this->AbortGroup(ev);
+  }
+  else if ( mWriter_Store && ev->Good() )
+  {
+    // after rewriting everything, there are no transaction groups:
+    mWriter_Store->mStore_FirstCommitGroupPos = 0;
+    mWriter_Store->mStore_SecondCommitGroupPos = 0;
+  }
+  
+  stream->Flush(ev->AsMdbEnv());
+  nsIMdbFile* bud = mWriter_Bud;
+  if ( bud )
+  {
+    bud->Flush(ev->AsMdbEnv());
+    bud->BecomeTrunk(ev->AsMdbEnv());
+    nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_Bud);
+  }
+  else if ( !mWriter_Incremental ) // should have a bud?
+    this->NilWriterBudError(ev);
+    
+  mWriter_Phase = morkWriter_kPhaseWritingDone; // stop always
+  mWriter_DoneCount = mWriter_TotalCount;
+  
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::OnWritingDone(morkEnv* ev)
+{
+  mWriter_DoneCount = mWriter_TotalCount;
+  ev->NewWarning("writing is done");
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::PutTableChange(morkEnv* ev, const morkTableChange* inChange)
+{
+  nsIMdbEnv *mdbev = ev->AsMdbEnv();
+  if ( inChange->IsAddRowTableChange() )
+  {
+    this->PutRow(ev, inChange->mTableChange_Row ); // row alone means add
+  }
+  else if ( inChange->IsCutRowTableChange() )
+  {
+    mWriter_Stream->Putc(ev, '-'); // prefix '-' indicates cut row
+    ++mWriter_LineSize;
+    this->PutRow(ev, inChange->mTableChange_Row );
+  }
+  else if ( inChange->IsMoveRowTableChange() )
+  {
+    this->PutRow(ev, inChange->mTableChange_Row );
+    char buf[ 64 ];
+    char* p = buf;
+    *p++ = '!'; // for moves, position is indicated by prefix '!'
+    mork_size posSize = ev->TokenAsHex(p, inChange->mTableChange_Pos);
+    p += posSize;
+    *p++ = ' ';
+    mork_size bytesWritten;
+    mWriter_Stream->Write(mdbev, buf, posSize + 2, &bytesWritten);
+    mWriter_LineSize += bytesWritten;
+  }
+  else
+    inChange->UnknownChangeError(ev);
+  
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::PutTable(morkEnv* ev, morkTable* ioTable)
+{
+  if ( ev->Good() )
+    this->StartTable(ev, ioTable);
+    
+  if ( ev->Good() )
+  {
+    if ( ioTable->IsTableRewrite() || mWriter_NeedDirtyAll )
+    {
+      morkArray* array = &ioTable->mTable_RowArray; // vector of rows
+      mork_fill fill = array->mArray_Fill; // count of rows
+      morkRow** rows = (morkRow**) array->mArray_Slots;
+      if ( rows && fill )
+      {
+        morkRow** end = rows + fill;
+        while ( rows < end && ev->Good() )
+        {
+          morkRow* r = *rows++; // next row to consider
+          this->PutRow(ev, r);
+        }
+      }
+    }
+    else // incremental write only table changes
+    {
+      morkList* list = &ioTable->mTable_ChangeList;
+      morkNext* next = list->GetListHead();
+      while ( next && ev->Good() )
+      {
+        this->PutTableChange(ev, (morkTableChange*) next);
+        next = next->GetNextLink();
+      }
+    }
+  }
+    
+  if ( ev->Good() )
+    this->EndTable(ev);
+  
+  ioTable->SetTableClean(ev); // note this also cleans change list
+  mWriter_TableRowScope = 0;
+
+  ++mWriter_DoneCount;
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::PutTableDict(morkEnv* ev, morkTable* ioTable)
+{
+  morkRowSpace* space = ioTable->mTable_RowSpace;
+  mWriter_TableRowScope = space->SpaceScope();
+  mWriter_TableForm = 0;     // (f=iso-8859-1)
+  mWriter_TableAtomScope = 'v'; // (a=v)
+  mWriter_TableKind = ioTable->mTable_Kind;
+  
+  mWriter_RowForm = mWriter_TableForm;
+  mWriter_RowAtomScope = mWriter_TableAtomScope;
+  mWriter_RowScope = mWriter_TableRowScope;
+  
+  mWriter_DictForm = mWriter_TableForm;
+  mWriter_DictAtomScope = mWriter_TableAtomScope;
+  
+  // if ( ev->Good() )
+  //  this->StartDict(ev); // delay as long as possible
+
+  if ( ev->Good() )
+  {
+    morkRow* r = ioTable->mTable_MetaRow;
+    if ( r )
+    {
+      if ( r->IsRow() )
+        this->PutRowDict(ev, r);
+      else
+        r->NonRowTypeError(ev);
+    }
+    morkArray* array = &ioTable->mTable_RowArray; // vector of rows
+    mork_fill fill = array->mArray_Fill; // count of rows
+    morkRow** rows = (morkRow**) array->mArray_Slots;
+    if ( rows && fill )
+    {
+      morkRow** end = rows + fill;
+      while ( rows < end && ev->Good() )
+      {
+        r = *rows++; // next row to consider
+        if ( r && r->IsRow() )
+          this->PutRowDict(ev, r);
+        else
+          r->NonRowTypeError(ev);
+      }
+    }
+    // we may have a change for a row which is no longer in the
+    // table, but contains a cell with something not in the dictionary.
+    // So, loop through the rows in the change log, writing out any
+    // dirty dictionary elements.
+    morkList* list = &ioTable->mTable_ChangeList;
+    morkNext* next = list->GetListHead();
+    while ( next && ev->Good() )
+    {
+      r = ((morkTableChange*) next)->mTableChange_Row;
+      if  ( r && r->IsRow() )
+        this->PutRowDict(ev, r);
+      next = next->GetNextLink();
+    }
+  }
+  if ( ev->Good() )
+    this->EndDict(ev);
+  
+  return ev->Good();
+}
+  
+void
+morkWriter::WriteTokenToTokenMetaCell(morkEnv* ev,
+  mork_token inCol, mork_token inValue)
+{
+  morkStream* stream = mWriter_Stream;
+  mork_bool isKindCol = ( morkStore_kKindColumn == inCol );
+  mork_u1 valSep = (mork_u1) (( isKindCol )? '^' : '=');
+  
+  char buf[ 128 ]; // buffer for staging the two hex IDs
+  char* p = buf;
+
+  mork_size bytesWritten;
+  if ( inCol < 0x80 )
+  {
+    stream->Putc(ev, '(');
+    stream->Putc(ev, (char) inCol);
+    stream->Putc(ev, valSep);
+  }
+  else
+  {
+    *p++ = '('; // we always start with open paren
+    
+    *p++ = '^'; // indicates col is hex ID
+    mork_size colSize = ev->TokenAsHex(p, inCol);
+    p += colSize;
+    *p++ = (char) valSep;
+    stream->Write(ev->AsMdbEnv(), buf, colSize + 3, &bytesWritten);
+    
+    mWriter_LineSize += bytesWritten;
+  }
+
+  if ( isKindCol )
+  {
+    p = buf;
+    mork_size valSize = ev->TokenAsHex(p, inValue);
+    p += valSize;
+    *p++ = ':';
+    *p++ = 'c';
+    *p++ = ')';
+    stream->Write(ev->AsMdbEnv(), buf, valSize + 3, &bytesWritten);
+    mWriter_LineSize += bytesWritten;
+  }
+  else
+  {
+    this->IndentAsNeeded(ev, morkWriter_kTableMetaCellValueDepth);
+    mdbYarn* yarn = &mWriter_ColYarn;
+    // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf;
+    mWriter_Store->TokenToString(ev, inValue, yarn);
+    this->WriteYarn(ev, yarn);
+    stream->Putc(ev, ')');
+    ++mWriter_LineSize;
+  }
+  
+  // mork_fill fill = yarn->mYarn_Fill;
+  // yarnBuf[ fill ] = ')'; // append terminator
+  // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')'
+}
+  
+void
+morkWriter::WriteStringToTokenDictCell(morkEnv* ev,
+  const char* inCol, mork_token inValue)
+  // Note inCol should begin with '(' and end with '=', with col in between.
+{
+  morkStream* stream = mWriter_Stream;
+  mWriter_LineSize += stream->PutString(ev, inCol);
+
+  this->IndentAsNeeded(ev, morkWriter_kDictMetaCellValueDepth);
+  mdbYarn* yarn = &mWriter_ColYarn;
+  // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf;
+  mWriter_Store->TokenToString(ev, inValue, yarn);
+  this->WriteYarn(ev, yarn);
+  stream->Putc(ev, ')');
+  ++mWriter_LineSize;
+  
+  // mork_fill fill = yarn->mYarn_Fill;
+  // yarnBuf[ fill ] = ')'; // append terminator
+  // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')'
+}
+
+void
+morkWriter::ChangeDictAtomScope(morkEnv* ev, mork_scope inScope)
+{
+  if ( inScope != mWriter_DictAtomScope )
+  {
+    ev->NewWarning("unexpected atom scope change");
+    
+    morkStream* stream = mWriter_Stream;
+    if ( mWriter_LineSize )
+      stream->PutLineBreak(ev);
+    mWriter_LineSize = 0;
+
+    char buf[ 128 ]; // buffer for staging the two hex IDs
+    char* p = buf;
+    *p++ = '<'; // we always start with open paren
+    *p++ = '('; // we always start with open paren
+    *p++ = (char) morkStore_kAtomScopeColumn;
+
+    mork_size scopeSize = 1; // default to one byte
+    if ( inScope >= 0x80 )
+    {
+      *p++ = '^'; // indicates col is hex ID
+      scopeSize = ev->TokenAsHex(p, inScope);
+      p += scopeSize;
+    }
+    else
+    {
+      *p++ = '='; // indicates col is imm byte
+      *p++ = (char) (mork_u1) inScope;
+    }
+
+    *p++ = ')';
+    *p++ = '>';
+    *p = 0;
+
+    mork_size pending = scopeSize + 6;
+    this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
+    mork_size bytesWritten;
+
+    stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
+    mWriter_LineSize += bytesWritten;
+      
+    mWriter_DictAtomScope = inScope;
+  }
+}
+
+void
+morkWriter::ChangeRowForm(morkEnv* ev, mork_cscode inNewForm)
+{
+  if ( inNewForm != mWriter_RowForm )
+  {
+    morkStream* stream = mWriter_Stream;
+    if ( mWriter_LineSize )
+      stream->PutLineBreak(ev);
+    mWriter_LineSize = 0;
+
+    char buf[ 128 ]; // buffer for staging the two hex IDs
+    char* p = buf;
+    *p++ = '['; // we always start with open bracket
+    *p++ = '('; // we always start with open paren
+    *p++ = (char) morkStore_kFormColumn;
+
+    mork_size formSize = 1; // default to one byte
+    if (! morkCh_IsValue(inNewForm))
+    {
+      *p++ = '^'; // indicates col is hex ID
+      formSize = ev->TokenAsHex(p, inNewForm);
+      p += formSize;
+    }
+    else
+    {
+      *p++ = '='; // indicates col is imm byte
+      *p++ = (char) (mork_u1) inNewForm;
+    }
+    
+    *p++ = ')';
+    *p++ = ']';
+    *p = 0;
+
+    mork_size pending = formSize + 6;
+    this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
+    mork_size bytesWritten;
+    stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
+    mWriter_LineSize += bytesWritten;
+      
+    mWriter_RowForm = inNewForm;
+  }
+}
+
+void
+morkWriter::ChangeDictForm(morkEnv* ev, mork_cscode inNewForm)
+{
+  if ( inNewForm != mWriter_DictForm )
+  {
+    morkStream* stream = mWriter_Stream;
+    if ( mWriter_LineSize )
+      stream->PutLineBreak(ev);
+    mWriter_LineSize = 0;
+
+    char buf[ 128 ]; // buffer for staging the two hex IDs
+    char* p = buf;
+    *p++ = '<'; // we always start with open angle
+    *p++ = '('; // we always start with open paren
+    *p++ = (char) morkStore_kFormColumn;
+
+    mork_size formSize = 1; // default to one byte
+    if (! morkCh_IsValue(inNewForm))
+    {
+      *p++ = '^'; // indicates col is hex ID
+      formSize = ev->TokenAsHex(p, inNewForm);
+      p += formSize;
+    }
+    else
+    {
+      *p++ = '='; // indicates col is imm byte
+      *p++ = (char) (mork_u1) inNewForm;
+    }
+    
+    *p++ = ')';
+    *p++ = '>';
+    *p = 0;
+
+    mork_size pending = formSize + 6;
+    this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
+    
+    mork_size bytesWritten;
+    stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
+    mWriter_LineSize += bytesWritten;
+      
+    mWriter_DictForm = inNewForm;
+  }
+}
+
+void
+morkWriter::StartDict(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_DidStartDict )
+  {
+    stream->Putc(ev, '>'); // end dict
+    ++mWriter_LineSize;
+  }
+  mWriter_DidStartDict = morkBool_kTrue;
+  mWriter_DidEndDict = morkBool_kFalse;
+  
+  if ( mWriter_LineSize )
+    stream->PutLineBreak(ev);
+  mWriter_LineSize = 0;
+  
+  if ( mWriter_TableRowScope ) // blank line before table's dict?
+    stream->PutLineBreak(ev);
+    
+  if ( mWriter_DictForm || mWriter_DictAtomScope != 'v' )
+  {
+    stream->Putc(ev, '<');
+    stream->Putc(ev, ' ');
+    stream->Putc(ev, '<');
+    mWriter_LineSize = 3;
+    if ( mWriter_DictForm )
+      this->WriteStringToTokenDictCell(ev, "(f=", mWriter_DictForm);
+    if ( mWriter_DictAtomScope != 'v' )
+      this->WriteStringToTokenDictCell(ev, "(a=", mWriter_DictAtomScope);
+  
+    stream->Putc(ev, '>');
+    ++mWriter_LineSize;
+
+    mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth);
+  }
+  else
+  {
+    stream->Putc(ev, '<');
+    // stream->Putc(ev, ' ');
+    ++mWriter_LineSize;
+  }
+}
+
+void
+morkWriter::EndDict(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  if ( mWriter_DidStartDict )
+  {
+    stream->Putc(ev, '>'); // end dict
+    ++mWriter_LineSize;
+  }
+  mWriter_DidStartDict = morkBool_kFalse;
+  mWriter_DidEndDict = morkBool_kTrue;
+}
+
+void
+morkWriter::StartTable(morkEnv* ev, morkTable* ioTable)
+{
+  mdbOid toid; // to receive table oid
+  ioTable->GetTableOid(ev, &toid);
+  
+  if ( ev->Good() )
+  {
+    morkStream* stream = mWriter_Stream;
+    if ( mWriter_LineSize )
+      stream->PutLineBreak(ev);
+    mWriter_LineSize = 0;
+    // stream->PutLineBreak(ev);
+
+    char buf[ 64 + 16 ]; // buffer for staging hex
+    char* p = buf;
+    *p++ = '{'; // punct 1
+    mork_size punctSize = (mWriter_BeVerbose) ? 10 : 3; // counting "{ {/*r=*/ "
+
+    if ( ioTable->IsTableRewrite() && mWriter_Incremental )
+    {
+      *p++ = '-';
+      ++punctSize; // counting '-' // punct ++
+      ++mWriter_LineSize;
+    }
+    mork_size oidSize = ev->OidAsHex(p, toid);
+    p += oidSize;
+    *p++ = ' '; // punct 2
+    *p++ = '{'; // punct 3
+    if (mWriter_BeVerbose)
+    {
+    
+      *p++ = '/'; // punct=4
+      *p++ = '*'; // punct=5
+      *p++ = 'r'; // punct=6
+      *p++ = '='; // punct=7
+
+      mork_token tableUses = (mork_token) ioTable->mTable_GcUses;
+      mork_size usesSize = ev->TokenAsHex(p, tableUses);
+      punctSize += usesSize;
+      p += usesSize;
+    
+      *p++ = '*'; // punct=8
+      *p++ = '/'; // punct=9
+      *p++ = ' '; // punct=10
+    }
+    mork_size bytesWritten;
+
+    stream->Write(ev->AsMdbEnv(), buf, oidSize + punctSize, &bytesWritten);
+    mWriter_LineSize += bytesWritten;
+
+    mork_kind tk = mWriter_TableKind;
+    if ( tk )
+    {
+      this->IndentAsNeeded(ev, morkWriter_kTableMetaCellDepth);
+      this->WriteTokenToTokenMetaCell(ev, morkStore_kKindColumn, tk);
+    }
+      
+    stream->Putc(ev, '('); // start 's' col cell
+    stream->Putc(ev, 's'); // column
+    stream->Putc(ev, '='); // column
+    mWriter_LineSize += 3;
+
+    int prio = (int) ioTable->mTable_Priority;
+    if ( prio > 9 ) // need to force down to max decimal digit?
+      prio = 9;
+    prio += '0'; // add base digit zero
+    stream->Putc(ev, prio); // priority: (s=0
+    ++mWriter_LineSize;
+    
+    if ( ioTable->IsTableUnique() )
+    {
+      stream->Putc(ev, 'u'); // (s=0u
+      ++mWriter_LineSize;
+    }
+    if ( ioTable->IsTableVerbose() )
+    {
+      stream->Putc(ev, 'v'); // (s=0uv
+      ++mWriter_LineSize;
+    }
+    
+    // stream->Putc(ev, ':'); // (s=0uv:
+    // stream->Putc(ev, 'c'); // (s=0uv:c
+    stream->Putc(ev, ')'); // end 's' col cell (s=0uv:c)
+    mWriter_LineSize += 1; // maybe 3 if we add ':' and 'c'
+
+    morkRow* r = ioTable->mTable_MetaRow;
+    if ( r )
+    {
+      if ( r->IsRow() )
+      {
+        mWriter_SuppressDirtyRowNewline = morkBool_kTrue;
+        this->PutRow(ev, r);
+      }
+      else
+        r->NonRowTypeError(ev);
+    }
+    
+    stream->Putc(ev, '}'); // end meta
+    ++mWriter_LineSize;
+    
+    if ( mWriter_LineSize < mWriter_MaxIndent )
+    {
+      stream->Putc(ev, ' '); // nice white space
+      ++mWriter_LineSize;
+    }
+  }
+}
+
+void
+morkWriter::EndTable(morkEnv* ev)
+{
+  morkStream* stream = mWriter_Stream;
+  stream->Putc(ev, '}'); // end table
+  ++mWriter_LineSize;
+
+  mWriter_TableAtomScope = 'v'; // (a=v)
+}
+
+mork_bool
+morkWriter::PutRowDict(morkEnv* ev, morkRow* ioRow)
+{
+  mWriter_RowForm = mWriter_TableForm;
+
+  morkCell* cells = ioRow->mRow_Cells;
+  if ( cells )
+  {
+    morkStream* stream = mWriter_Stream;
+    mdbYarn yarn; // to ref content inside atom
+    char buf[ 64 ]; // buffer for staging the dict alias hex ID
+    char* idBuf = buf + 1; // where the id always starts
+    buf[ 0 ] = '('; // we always start with open paren
+
+    morkCell* end = cells + ioRow->mRow_Length;
+    --cells; // prepare for preincrement:
+    while ( ++cells < end && ev->Good() )
+    {
+      morkAtom* atom = cells->GetAtom();
+      if ( atom && atom->IsAtomDirty() )
+      {
+        if ( atom->IsBook() ) // is it possible to write atom ID?
+        {
+          if ( !this->DidStartDict() )
+          {
+            this->StartDict(ev);
+            if ( ev->Bad() )
+              break;
+          }
+          atom->SetAtomClean(); // neutralize change
+          
+          this->IndentAsNeeded(ev, morkWriter_kDictAliasDepth);
+          morkBookAtom* ba = (morkBookAtom*) atom;
+          mork_size size = ev->TokenAsHex(idBuf, ba->mBookAtom_Id);
+          mork_size bytesWritten;
+          stream->Write(ev->AsMdbEnv(), buf, size+1, &bytesWritten); // '('
+          mWriter_LineSize += bytesWritten;
+
+          if ( atom->AliasYarn(&yarn) )
+          {
+            mork_scope atomScope = atom->GetBookAtomSpaceScope(ev);
+            if ( atomScope && atomScope != mWriter_DictAtomScope )
+              this->ChangeDictAtomScope(ev, atomScope);
+            
+            if ( mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm )
+              this->ChangeDictForm(ev, yarn.mYarn_Form);  
+      
+            mork_size pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop + 1;
+            this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth);
+              
+            stream->Putc(ev, '='); // start value
+            ++mWriter_LineSize;
+      
+            this->WriteYarn(ev, &yarn);
+
+            stream->Putc(ev, ')'); // end value
+            ++mWriter_LineSize;
+          }
+          else
+            atom->BadAtomKindError(ev);
+                      
+          ++mWriter_DoneCount;
+        }
+      }
+    }
+  }
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::IsYarnAllValue(const mdbYarn* inYarn)
+{
+  mork_fill fill = inYarn->mYarn_Fill;
+  const mork_u1* buf = (const mork_u1*) inYarn->mYarn_Buf;
+  const mork_u1* end = buf + fill;
+  --buf; // prepare for preincrement
+  while ( ++buf < end )
+  {
+    mork_ch c = *buf;
+    if ( !morkCh_IsValue(c) )
+      return morkBool_kFalse;
+  }
+  return morkBool_kTrue;
+}
+
+mork_bool
+morkWriter::PutVerboseCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal)
+{
+  morkStream* stream = mWriter_Stream;
+  morkStore* store = mWriter_Store;
+
+  mdbYarn* colYarn = &mWriter_ColYarn;
+  
+  morkAtom* atom = (inWithVal)? ioCell->GetAtom() : (morkAtom*) 0;
+  
+  mork_column col = ioCell->GetColumn();
+  store->TokenToString(ev, col, colYarn);
+  
+  mdbYarn yarn; // to ref content inside atom
+  atom->AliasYarn(&yarn); // works even when atom==nil
+  
+  if ( yarn.mYarn_Form != mWriter_RowForm )
+    this->ChangeRowForm(ev, yarn.mYarn_Form);
+
+  mork_size pending = yarn.mYarn_Fill + colYarn->mYarn_Fill +
+     morkWriter_kYarnEscapeSlop + 3;
+  this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
+
+  stream->Putc(ev, '('); // start cell
+  ++mWriter_LineSize;
+
+  this->WriteYarn(ev, colYarn); // column
+  
+  pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop;
+  this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellValueDepth);
+  stream->Putc(ev, '=');
+  ++mWriter_LineSize;
+  
+  this->WriteYarn(ev, &yarn); // value
+  
+  stream->Putc(ev, ')'); // end cell
+  ++mWriter_LineSize;
+
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::PutVerboseRowCells(morkEnv* ev, morkRow* ioRow)
+{
+  morkCell* cells = ioRow->mRow_Cells;
+  if ( cells )
+  {
+
+    morkCell* end = cells + ioRow->mRow_Length;
+    --cells; // prepare for preincrement:
+    while ( ++cells < end && ev->Good() )
+    {
+      // note we prefer to avoid writing cells here with no value:
+      if ( cells->GetAtom() ) // does cell have any value?
+        this->PutVerboseCell(ev, cells, /*inWithVal*/ morkBool_kTrue);
+    }
+  }
+  return ev->Good();
+}
+
+
+mork_bool
+morkWriter::PutCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal)
+{
+  morkStream* stream = mWriter_Stream;
+  char buf[ 128 ]; // buffer for staging hex ids
+  char* idBuf = buf + 2; // where the id always starts
+  buf[ 0 ] = '('; // we always start with open paren
+  buf[ 1 ] = '^'; // column is always a hex ID
+  
+  mork_size colSize = 0; // the size of col hex ID
+  mork_size bytesWritten;
+  
+  morkAtom* atom = (inWithVal)? ioCell->GetAtom() : (morkAtom*) 0;
+  
+  mork_column col = ioCell->GetColumn();
+  char* p = idBuf;
+  colSize = ev->TokenAsHex(p, col);
+  p += colSize;
+
+  mdbYarn yarn; // to ref content inside atom
+  atom->AliasYarn(&yarn); // works even when atom==nil
+  
+  if ( yarn.mYarn_Form != mWriter_RowForm )
+    this->ChangeRowForm(ev, yarn.mYarn_Form);
+  
+  if ( atom && atom->IsBook() ) // is it possible to write atom ID?
+  {
+    this->IndentAsNeeded(ev, morkWriter_kRowCellDepth);
+    *p++ = '^';
+    morkBookAtom* ba = (morkBookAtom*) atom;
+
+    mork_size valSize = ev->TokenAsHex(p, ba->mBookAtom_Id);
+    mork_fill yarnFill = yarn.mYarn_Fill;
+    mork_bool putImmYarn = ( yarnFill <= valSize );
+    if ( putImmYarn )
+      putImmYarn = this->IsYarnAllValue(&yarn);
+    
+    if ( putImmYarn ) // value no bigger than id?
+    {
+      p[ -1 ] = '='; // go back and clobber '^' with '=' instead
+      if ( yarnFill )
+      {
+        MORK_MEMCPY(p, yarn.mYarn_Buf, yarnFill);
+        p += yarnFill;
+      }
+      *p++ = ')';
+      mork_size distance = (mork_size) (p - buf);
+      stream->Write(ev->AsMdbEnv(), buf, distance, &bytesWritten);
+      mWriter_LineSize += bytesWritten;
+    }
+    else
+    {
+      p += valSize;
+      *p = ')';
+      stream->Write(ev->AsMdbEnv(), buf, colSize + valSize + 4, &bytesWritten);
+      mWriter_LineSize += bytesWritten;
+    }
+
+    if ( atom->IsAtomDirty() )
+    {
+      atom->SetAtomClean();
+      ++mWriter_DoneCount;
+    }
+  }
+  else // must write an anonymous atom
+  {
+    mork_size pending = yarn.mYarn_Fill + colSize +
+      morkWriter_kYarnEscapeSlop + 2;
+    this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
+
+    mork_size bytesWritten;
+    stream->Write(ev->AsMdbEnv(), buf, colSize + 2, &bytesWritten);
+    mWriter_LineSize += bytesWritten;
+
+    pending -= ( colSize + 2 );
+    this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
+    stream->Putc(ev, '=');
+    ++mWriter_LineSize;
+    
+    this->WriteYarn(ev, &yarn);
+    stream->Putc(ev, ')'); // end cell
+    ++mWriter_LineSize;
+  }
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::PutRowCells(morkEnv* ev, morkRow* ioRow)
+{
+  morkCell* cells = ioRow->mRow_Cells;
+  if ( cells )
+  {
+    morkCell* end = cells + ioRow->mRow_Length;
+    --cells; // prepare for preincrement:
+    while ( ++cells < end && ev->Good() )
+    {
+      // note we prefer to avoid writing cells here with no value:
+      if ( cells->GetAtom() ) // does cell have any value?
+        this->PutCell(ev, cells, /*inWithVal*/ morkBool_kTrue);
+    }
+  }
+  return ev->Good();
+}
+
+mork_bool
+morkWriter::PutRow(morkEnv* ev, morkRow* ioRow)
+{
+  if ( ioRow && ioRow->IsRow() )
+  {
+    mWriter_RowForm = mWriter_TableForm;
+
+    mork_size bytesWritten;
+    morkStream* stream = mWriter_Stream;
+    char buf[ 128 + 16 ]; // buffer for staging hex
+    char* p = buf;
+    mdbOid* roid = &ioRow->mRow_Oid;
+    mork_size ridSize = 0;
+    
+    mork_scope tableScope = mWriter_TableRowScope;
+
+    if ( ioRow->IsRowDirty() )
+    {
+      if ( mWriter_SuppressDirtyRowNewline || !mWriter_LineSize )
+        mWriter_SuppressDirtyRowNewline = morkBool_kFalse;
+      else
+      {
+        if ( tableScope ) // in a table?
+          mWriter_LineSize = stream->PutIndent(ev, morkWriter_kRowDepth);
+        else
+          mWriter_LineSize = stream->PutIndent(ev, 0); // no indent
+      }
+      
+//      mork_rid rid = roid->mOid_Id;
+      *p++ = '['; // start row punct=1
+      mork_size punctSize = (mWriter_BeVerbose) ? 9 : 1; // counting "[ /*r=*/ "
+      
+      mork_bool rowRewrite = ioRow->IsRowRewrite();
+            
+      if ( rowRewrite && mWriter_Incremental )
+      {
+        *p++ = '-';
+        ++punctSize; // counting '-'
+        ++mWriter_LineSize;
+      }
+
+      if ( tableScope && roid->mOid_Scope == tableScope )
+        ridSize = ev->TokenAsHex(p, roid->mOid_Id);
+      else
+        ridSize = ev->OidAsHex(p, *roid);
+      
+      p += ridSize;
+      
+      if (mWriter_BeVerbose)
+      {
+        *p++ = ' '; // punct=2
+        *p++ = '/'; // punct=3
+        *p++ = '*'; // punct=4
+        *p++ = 'r'; // punct=5
+        *p++ = '='; // punct=6
+
+        mork_size usesSize = ev->TokenAsHex(p, (mork_token) ioRow->mRow_GcUses);
+        punctSize += usesSize;
+        p += usesSize;
+      
+        *p++ = '*'; // punct=7
+        *p++ = '/'; // punct=8
+        *p++ = ' '; // punct=9
+      }
+      stream->Write(ev->AsMdbEnv(), buf, ridSize + punctSize, &bytesWritten);
+      mWriter_LineSize += bytesWritten;
+      
+      // special case situation where row puts exactly one column:
+      if ( !rowRewrite && mWriter_Incremental && ioRow->HasRowDelta() )
+      {
+        mork_column col = ioRow->GetDeltaColumn();
+        morkCell dummy(col, morkChange_kNil, (morkAtom*) 0);
+        morkCell* cell = 0;
+        
+        mork_bool withVal = ( ioRow->GetDeltaChange() != morkChange_kCut );
+        
+        if ( withVal )
+        {
+          mork_pos cellPos = 0; // dummy pos
+          cell = ioRow->GetCell(ev, col, &cellPos);
+        }
+        if ( !cell )
+          cell = &dummy;
+          
+        if ( mWriter_BeVerbose )
+          this->PutVerboseCell(ev, cell, withVal);
+        else
+          this->PutCell(ev, cell, withVal);
+      }
+      else // put entire row?
+      {
+        if ( mWriter_BeVerbose )
+          this->PutVerboseRowCells(ev, ioRow); // write all, verbosely
+        else
+          this->PutRowCells(ev, ioRow); // write all, hex notation
+      }
+        
+      stream->Putc(ev, ']'); // end row
+      ++mWriter_LineSize;
+    }
+    else
+    {
+      this->IndentAsNeeded(ev, morkWriter_kRowDepth);
+
+      if ( tableScope && roid->mOid_Scope == tableScope )
+        ridSize = ev->TokenAsHex(p, roid->mOid_Id);
+      else
+        ridSize = ev->OidAsHex(p, *roid);
+
+      stream->Write(ev->AsMdbEnv(), buf, ridSize, &bytesWritten);
+      mWriter_LineSize += bytesWritten;
+      stream->Putc(ev, ' ');
+      ++mWriter_LineSize;
+    }
+
+    ++mWriter_DoneCount;
+
+    ioRow->SetRowClean(); // try to do this at the very last
+  }
+  else
+    ioRow->NonRowTypeWarning(ev);
+  
+  return ev->Good();
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkWriter.h
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKWRITER_
+#define _MORKWRITER_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+#define morkWriter_kStreamBufSize /*i*/ (16 * 1024) /* buffer size for stream */ 
+
+#define morkDerived_kWriter  /*i*/ 0x5772 /* ascii 'Wr' */
+
+#define morkWriter_kPhaseNothingDone          0 /* nothing has yet been done */
+#define morkWriter_kPhaseDirtyAllDone         1 /* DirtyAll() is done */
+#define morkWriter_kPhasePutHeaderDone        2 /* PutHeader() is done */
+
+#define morkWriter_kPhaseRenumberAllDone      3 /* RenumberAll() is done */
+
+#define morkWriter_kPhaseStoreAtomSpaces      4 /*mWriter_StoreAtomSpacesIter*/
+#define morkWriter_kPhaseAtomSpaceAtomAids    5 /*mWriter_AtomSpaceAtomAidsIter*/
+
+#define morkWriter_kPhaseStoreRowSpacesTables 6 /*mWriter_StoreRowSpacesIter*/
+#define morkWriter_kPhaseRowSpaceTables       7 /*mWriter_RowSpaceTablesIter*/
+#define morkWriter_kPhaseTableRowArray        8 /*mWriter_TableRowArrayPos */
+
+#define morkWriter_kPhaseStoreRowSpacesRows   9 /*mWriter_StoreRowSpacesIter*/
+#define morkWriter_kPhaseRowSpaceRows        10 /*mWriter_RowSpaceRowsIter*/
+
+#define morkWriter_kPhaseContentDone         11 /* all content written */
+#define morkWriter_kPhaseWritingDone         12 /* everthing has been done */
+
+#define morkWriter_kCountNumberOfPhases      13 /* part of mWrite_TotalCount */
+
+#define morkWriter_kMaxColumnNameSize        128 /* longest writable col name */
+
+#define morkWriter_kMaxIndent 66 /* default value for mWriter_MaxIndent */
+#define morkWriter_kMaxLine   78 /* default value for mWriter_MaxLine */
+
+#define morkWriter_kYarnEscapeSlop  4 /* guess average yarn escape overhead */
+
+#define morkWriter_kTableMetaCellDepth 4 /* */
+#define morkWriter_kTableMetaCellValueDepth 6 /* */
+
+#define morkWriter_kDictMetaCellDepth 4 /* */
+#define morkWriter_kDictMetaCellValueDepth 6 /* */
+
+#define morkWriter_kDictAliasDepth 2 /* */
+#define morkWriter_kDictAliasValueDepth 4 /* */
+
+#define morkWriter_kRowDepth 2 /* */
+#define morkWriter_kRowCellDepth 4 /* */
+#define morkWriter_kRowCellValueDepth 6 /* */
+
+#define morkWriter_kGroupBufSize 64 /* */
+
+// v=1.1 retired on 23-Mar-99 (for metainfo one char column names)
+// v=1.2 retired on 20-Apr-99 (for ":c" suffix on table kind hex refs)
+// v=1.3 retired on 20-Apr-99 (for 1CE:m instead of ill-formed 1CE:^6D)
+#define morkWriter_kFileHeader "// <!-- <mdb:mork:z v=\"1.4\"/> -->"
+
+class morkWriter : public morkNode { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+  // nsIMdbHeap*     mNode_Heap;
+  // mork_able    mNode_Mutable; // can this node be modified?
+  // mork_load    mNode_Load;    // is this node clean or dirty?
+  // mork_base    mNode_Base;    // must equal morkBase_kNode
+  // mork_derived mNode_Derived; // depends on specific node subclass
+  // mork_access  mNode_Access;  // kOpen, kClosing, kShut, or kDead
+  // mork_usage   mNode_Usage;   // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_uses    mNode_Uses;    // refcount for strong refs
+  // mork_refs    mNode_Refs;    // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+  morkStore*   mWriter_Store;      // weak ref to committing store
+  nsIMdbFile*  mWriter_File;       // strong ref to store's file
+  nsIMdbFile*  mWriter_Bud;        // strong ref to bud of mWriter_File
+  morkStream*  mWriter_Stream;     // strong ref to stream on bud file
+  nsIMdbHeap*  mWriter_SlotHeap;   // strong ref to slot heap
+
+  // GroupIdentity should be based on mStore_CommitGroupIdentity:
+  mork_gid     mWriter_CommitGroupIdentity; // transaction ID number
+  
+  // GroupBuf holds a hex version of mWriter_CommitGroupIdentity:
+  char         mWriter_GroupBuf[ morkWriter_kGroupBufSize ];
+  mork_fill    mWriter_GroupBufFill; // actual bytes in GroupBuf
+  
+  mork_count   mWriter_TotalCount;  // count of all things to be written
+  mork_count   mWriter_DoneCount;   // count of things already written
+  
+  mork_size    mWriter_LineSize;  // length of current line being written
+  mork_size    mWriter_MaxIndent; // line size forcing a line break
+  mork_size    mWriter_MaxLine;   // line size forcing a value continuation
+  
+  mork_cscode  mWriter_TableForm;     // current charset metainfo
+  mork_scope   mWriter_TableAtomScope;   // current atom scope
+  mork_scope   mWriter_TableRowScope;    // current row scope
+  mork_kind    mWriter_TableKind;        // current table kind
+  
+  mork_cscode  mWriter_RowForm;         // current charset metainfo
+  mork_scope   mWriter_RowAtomScope;    // current atom scope
+  mork_scope   mWriter_RowScope;        // current row scope
+  
+  mork_cscode  mWriter_DictForm;      // current charset metainfo
+  mork_scope   mWriter_DictAtomScope;    // current atom scope
+ 
+  mork_bool    mWriter_NeedDirtyAll;  // need to call DirtyAll()
+  mork_bool    mWriter_Incremental;   // opposite of mWriter_NeedDirtyAll
+  mork_bool    mWriter_DidStartDict;  // true when a dict has been started
+  mork_bool    mWriter_DidEndDict;    // true when a dict has been ended
+
+  mork_bool    mWriter_SuppressDirtyRowNewline; // for table meta rows
+  mork_bool    mWriter_DidStartGroup; // true when a group has been started
+  mork_bool    mWriter_DidEndGroup;    // true when a group has been ended
+  mork_u1      mWriter_Phase;         // status of writing process
+
+  mork_bool    mWriter_BeVerbose; // driven by env and table verbose settings:
+  // mWriter_BeVerbose equals ( ev->mEnv_BeVerbose || table->IsTableVerbose() )
+  
+  mork_u1      mWriter_Pad[ 3 ];  // for u4 alignment
+
+  mork_pos     mWriter_TableRowArrayPos;  // index into mTable_RowArray
+   
+  char         mWriter_SafeNameBuf[ (morkWriter_kMaxColumnNameSize * 2) + 4 ];
+  // Note: extra four bytes in ColNameBuf means we can always append to yarn
+
+  char         mWriter_ColNameBuf[ morkWriter_kMaxColumnNameSize + 4 ];
+  // Note: extra four bytes in ColNameBuf means we can always append to yarn
+  
+  mdbYarn      mWriter_ColYarn; // a yarn to describe space in ColNameBuf:
+  // mYarn_Buf == mWriter_ColNameBuf, mYarn_Size == morkWriter_kMaxColumnNameSize
+  
+  mdbYarn      mWriter_SafeYarn; // a yarn to describe space in ColNameBuf:
+  // mYarn_Buf == mWriter_SafeNameBuf, mYarn_Size == (kMaxColumnNameSize * 2)
+
+  morkAtomSpaceMapIter  mWriter_StoreAtomSpacesIter;   // for mStore_AtomSpaces
+  morkAtomAidMapIter  mWriter_AtomSpaceAtomAidsIter; // for AtomSpace_AtomAids
+  
+  morkRowSpaceMapIter  mWriter_StoreRowSpacesIter;    // for mStore_RowSpaces
+  morkTableMapIter  mWriter_RowSpaceTablesIter;    // for mRowSpace_Tables
+  
+#ifdef MORK_ENABLE_PROBE_MAPS
+  morkRowProbeMapIter  mWriter_RowSpaceRowsIter; // for mRowSpace_Rows
+#else /*MORK_ENABLE_PROBE_MAPS*/
+  morkRowMapIter  mWriter_RowSpaceRowsIter;      // for mRowSpace_Rows
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+   
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseWriter()
+  virtual ~morkWriter(); // assert that close executed earlier
+  
+public: // morkWriter construction & destruction
+  morkWriter(morkEnv* ev, const morkUsage& inUsage,
+    nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbFile* ioFile,
+    nsIMdbHeap* ioSlotHeap);
+  void CloseWriter(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkWriter(const morkWriter& other);
+  morkWriter& operator=(const morkWriter& other);
+
+public: // dynamic type identification
+  mork_bool IsWriter() const
+  { return IsNode() && mNode_Derived == morkDerived_kWriter; }
+// } ===== end morkNode methods =====
+
+public: // typing & errors
+  static void NonWriterTypeError(morkEnv* ev);
+  static void NilWriterStoreError(morkEnv* ev);
+  static void NilWriterBudError(morkEnv* ev);
+  static void NilWriterStreamError(morkEnv* ev);
+  static void NilWriterFileError(morkEnv* ev);
+  static void UnsupportedPhaseError(morkEnv* ev);
+
+public: // utitlities
+  void ChangeRowForm(morkEnv* ev, mork_cscode inNewForm);
+  void ChangeDictForm(morkEnv* ev, mork_cscode inNewForm);
+  void ChangeDictAtomScope(morkEnv* ev, mork_scope inScope);
+
+public: // inlines
+  mork_bool DidStartDict() const { return mWriter_DidStartDict; }
+  mork_bool DidEndDict() const { return mWriter_DidEndDict; }
+  
+  void IndentAsNeeded(morkEnv* ev, mork_size inDepth)
+  { 
+    if ( mWriter_LineSize > mWriter_MaxIndent )
+      mWriter_LineSize = mWriter_Stream->PutIndent(ev, inDepth);
+  }
+  
+  void IndentOverMaxLine(morkEnv* ev,
+    mork_size inPendingSize, mork_size inDepth)
+  { 
+    if ( mWriter_LineSize + inPendingSize > mWriter_MaxLine )
+      mWriter_LineSize = mWriter_Stream->PutIndent(ev, inDepth);
+  }
+
+public: // delayed construction
+
+  void MakeWriterStream(morkEnv* ev); // give writer a suitable stream
+
+public: // iterative/asynchronous writing
+  
+  mork_bool WriteMore(morkEnv* ev); // call until IsWritingDone() is true
+  
+  mork_bool IsWritingDone() const // don't call WriteMore() any longer?
+  { return mWriter_Phase == morkWriter_kPhaseWritingDone; }
+
+public: // marking all content dirty
+  mork_bool DirtyAll(morkEnv* ev);
+  // DirtyAll() visits every store sub-object and marks 
+  // them dirty, including every table, row, cell, and atom.  The return
+  // equals ev->Good(), to show whether any error happened.  This method is
+  // intended for use in the beginning of a "compress commit" which writes
+  // all store content, whether dirty or not.  We dirty everything first so
+  // that later iterations over content can mark things clean as they are
+  // written, and organize the process of serialization so that objects are
+  // written only at need (because of being dirty).  Note the method can 
+  // stop early when any error happens, since this will abort any commit.
+
+public: // group commit transactions
+
+  mork_bool StartGroup(morkEnv* ev);
+  mork_bool CommitGroup(morkEnv* ev);
+  mork_bool AbortGroup(morkEnv* ev);
+
+public: // phase methods
+  mork_bool OnNothingDone(morkEnv* ev);
+  mork_bool OnDirtyAllDone(morkEnv* ev);
+  mork_bool OnPutHeaderDone(morkEnv* ev);
+
+  mork_bool OnRenumberAllDone(morkEnv* ev);
+
+  mork_bool OnStoreAtomSpaces(morkEnv* ev);
+  mork_bool OnAtomSpaceAtomAids(morkEnv* ev);
+
+  mork_bool OnStoreRowSpacesTables(morkEnv* ev);
+  mork_bool OnRowSpaceTables(morkEnv* ev);
+  mork_bool OnTableRowArray(morkEnv* ev);
+
+  mork_bool OnStoreRowSpacesRows(morkEnv* ev);
+  mork_bool OnRowSpaceRows(morkEnv* ev);
+
+  mork_bool OnContentDone(morkEnv* ev);
+  mork_bool OnWritingDone(morkEnv* ev);
+
+public: // writing dict items first pass
+  mork_bool PutTableDict(morkEnv* ev, morkTable* ioTable);
+  mork_bool PutRowDict(morkEnv* ev, morkRow* ioRow);
+
+public: // writing node content second pass
+  mork_bool PutTable(morkEnv* ev, morkTable* ioTable);
+  mork_bool PutRow(morkEnv* ev, morkRow* ioRow);
+  mork_bool PutRowCells(morkEnv* ev, morkRow* ioRow);
+  mork_bool PutVerboseRowCells(morkEnv* ev, morkRow* ioRow);
+  
+  mork_bool PutCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal);
+  mork_bool PutVerboseCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal);
+  
+  mork_bool PutTableChange(morkEnv* ev, const morkTableChange* inChange);
+
+public: // other writer methods
+
+  mork_bool IsYarnAllValue(const mdbYarn* inYarn);
+
+  mork_size WriteYarn(morkEnv* ev, const mdbYarn* inYarn);
+  // return number of atom bytes written on the current line (which
+  // implies that escaped line breaks will make the size value smaller
+  // than the entire yarn's size, since only part goes on a last line).
+
+  mork_size WriteAtom(morkEnv* ev, const morkAtom* inAtom);
+  // return number of atom bytes written on the current line (which
+  // implies that escaped line breaks will make the size value smaller
+  // than the entire atom's size, since only part goes on a last line).
+
+  void WriteAllStoreTables(morkEnv* ev);
+  void WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace);
+  
+  void WriteTokenToTokenMetaCell(morkEnv* ev, mork_token inCol,
+    mork_token inValue);
+  void WriteStringToTokenDictCell(morkEnv* ev, const char* inCol, 
+    mork_token inValue);
+  // Note inCol should begin with '(' and end with '=', with col in between.
+
+  void StartDict(morkEnv* ev);
+  void EndDict(morkEnv* ev);
+
+  void StartTable(morkEnv* ev, morkTable* ioTable);
+  void EndTable(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakWriter(morkWriter* me,
+    morkEnv* ev, morkWriter** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongWriter(morkWriter* me,
+    morkEnv* ev, morkWriter** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTABLEROWCURSOR_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkYarn.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKYARN_
+#include "morkYarn.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` ````` 
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkYarn::CloseMorkNode(morkEnv* ev) /*i*/ // CloseYarn() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseYarn(ev);
+    this->MarkShut();
+  }
+}
+
+/*public virtual*/
+morkYarn::~morkYarn() /*i*/ // assert CloseYarn() executed earlier
+{
+  MORK_ASSERT(mYarn_Body.mYarn_Buf==0);
+}
+
+/*public non-poly*/
+morkYarn::morkYarn(morkEnv* ev, /*i*/
+  const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+  : morkNode(ev, inUsage, ioHeap)
+{
+  if ( ev->Good() )
+    mNode_Derived = morkDerived_kYarn;
+}
+
+/*public non-poly*/ void
+morkYarn::CloseYarn(morkEnv* ev) /*i*/ // called by CloseMorkNode();
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+      this->MarkShut();
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` ````` 
+
+/*static*/ void
+morkYarn::NonYarnTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkYarn");
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkYarn.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKYARN_
+#define _MORKYARN_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+#define morkDerived_kYarn     /*i*/ 0x7952 /* ascii 'yR' */
+
+/*| morkYarn: a reference counted nsIMdbYarn C struct.  This is for use in those
+**| few cases where single instances of reference counted buffers are needed
+**| in Mork, and we expect few enough instances that overhead is not a factor
+**| in decided whether to use such a thing.
+|*/
+class morkYarn : public morkNode { // refcounted yarn
+  
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+  mdbYarn  mYarn_Body;
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseYarn() only if open
+  virtual ~morkYarn(); // assert that CloseYarn() executed earlier
+  
+public: // morkYarn construction & destruction
+  morkYarn(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+  void CloseYarn(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+  morkYarn(const morkYarn& other);
+  morkYarn& operator=(const morkYarn& other);
+
+public: // dynamic type identification
+  mork_bool IsYarn() const
+  { return IsNode() && mNode_Derived == morkDerived_kYarn; }
+// } ===== end morkNode methods =====
+
+public: // typing
+  static void NonYarnTypeError(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+  static void SlotWeakYarn(morkYarn* me,
+    morkEnv* ev, morkYarn** ioSlot)
+  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+  
+  static void SlotStrongYarn(morkYarn* me,
+    morkEnv* ev, morkYarn** ioSlot)
+  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKYARN_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkZone.cpp
@@ -0,0 +1,577 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKZONE_
+#include "morkZone.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// { ===== begin morkNode interface =====
+// public: // morkNode virtual methods
+void morkZone::CloseMorkNode(morkEnv* ev) // CloseZone() only if open
+{
+  if ( this->IsOpenNode() )
+  {
+    this->MarkClosing();
+    this->CloseZone(ev);
+    this->MarkShut();
+  }
+}
+
+morkZone::~morkZone() // assert that CloseZone() executed earlier
+{
+  MORK_ASSERT(this->IsShutNode());
+}
+
+// public: // morkMap construction & destruction
+morkZone::morkZone(morkEnv* ev, const morkUsage& inUsage,
+  nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioZoneHeap)
+: morkNode(ev, inUsage, ioNodeHeap)
+, mZone_Heap( 0 )
+, mZone_HeapVolume( 0 )
+, mZone_BlockVolume( 0 )
+, mZone_RunVolume( 0 )
+, mZone_ChipVolume( 0 )
+  
+, mZone_FreeOldRunVolume( 0 )
+  
+, mZone_HunkCount( 0 )
+, mZone_FreeOldRunCount( 0 )
+
+, mZone_HunkList( 0 )
+, mZone_FreeOldRunList( 0 )
+  
+, mZone_At( 0 )
+, mZone_AtSize( 0 )
+    
+  // morkRun*     mZone_FreeRuns[ morkZone_kBuckets + 1 ];
+{
+
+  morkRun** runs = mZone_FreeRuns;
+  morkRun** end = runs + (morkZone_kBuckets + 1); // one past last slot
+  --runs; // prepare for preincrement
+  while ( ++runs < end ) // another slot in array?
+    *runs = 0; // clear all the slots
+  
+  if ( ev->Good() )
+  {
+    if ( ioZoneHeap )
+    {
+      nsIMdbHeap_SlotStrongHeap(ioZoneHeap, ev, &mZone_Heap);
+      if ( ev->Good() )
+        mNode_Derived = morkDerived_kZone;
+    }
+    else
+      ev->NilPointerError();
+  }
+}
+
+void morkZone::CloseZone(morkEnv* ev) // called by CloseMorkNode()
+{
+  if ( this )
+  {
+    if ( this->IsNode() )
+    {
+      nsIMdbHeap* heap = mZone_Heap;
+      if ( heap )
+      {
+        morkHunk* hunk = 0;
+        nsIMdbEnv* mev = ev->AsMdbEnv();
+        
+        morkHunk* next = mZone_HunkList;
+        while ( ( hunk = next ) != 0 )
+        {
+#ifdef morkHunk_USE_TAG_SLOT
+          if ( !hunk->HunkGoodTag()  )
+            hunk->BadHunkTagWarning(ev);
+#endif /* morkHunk_USE_TAG_SLOT */
+
+          next = hunk->HunkNext();
+          heap->Free(mev, hunk);
+        }
+      }
+      nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mZone_Heap);
+      this->MarkShut();
+    }
+    else
+      this->NonNodeError(ev);
+  }
+  else
+    ev->NilPointerError();
+}
+
+// } ===== end morkNode methods =====
+
+/*static*/ void
+morkZone::NonZoneTypeError(morkEnv* ev)
+{
+  ev->NewError("non morkZone");
+}
+
+/*static*/ void
+morkZone::NilZoneHeapError(morkEnv* ev)
+{
+  ev->NewError("nil mZone_Heap");
+}
+
+/*static*/ void
+morkHunk::BadHunkTagWarning(morkEnv* ev)
+{
+  ev->NewWarning("bad mHunk_Tag");
+}
+
+/*static*/ void
+morkRun::BadRunTagError(morkEnv* ev)
+{
+  ev->NewError("bad mRun_Tag");
+}
+
+/*static*/ void
+morkRun::RunSizeAlignError(morkEnv* ev)
+{
+  ev->NewError("bad RunSize() alignment");
+}
+
+// { ===== begin morkZone methods =====
+
+
+mork_size morkZone::zone_grow_at(morkEnv* ev, mork_size inNeededSize)
+{
+  mZone_At = 0;       // remove any ref to current hunk
+  mZone_AtSize = 0;   // zero available bytes in current hunk
+  
+  mork_size runSize = 0; // actual size of a particular run
+  
+  // try to find a run in old run list with at least inNeededSize bytes:
+  morkRun* run = mZone_FreeOldRunList; // cursor in list scan
+  morkRun* prev = 0; // the node before run in the list scan
+ 
+  while ( run ) // another run in list to check?
+  {
+    morkOldRun* oldRun = (morkOldRun*) run;
+    mork_size oldSize = oldRun->OldSize();
+    if ( oldSize >= inNeededSize ) // found one big enough?
+    {
+      runSize = oldSize;
+      break; // end while loop early
+    }
+    prev = run; // remember last position in singly linked list
+    run = run->RunNext(); // advance cursor to next node in list
+  }
+  if ( runSize && run ) // found a usable old run?
+  {
+    morkRun* next = run->RunNext();
+    if ( prev ) // another node in free list precedes run?
+      prev->RunSetNext(next); // unlink run
+    else
+      mZone_FreeOldRunList = next; // unlink run from head of list
+      
+    morkOldRun *oldRun = (morkOldRun *) run;
+    oldRun->OldSetSize(runSize);
+    mZone_At = (mork_u1*) run;
+    mZone_AtSize = runSize;
+
+#ifdef morkZone_CONFIG_DEBUG
+#ifdef morkZone_CONFIG_ALIGN_8
+    mork_ip lowThree = ((mork_ip) mZone_At) & 7;
+    if ( lowThree ) // not 8 byte aligned?
+#else /*morkZone_CONFIG_ALIGN_8*/
+    mork_ip lowTwo = ((mork_ip) mZone_At) & 3;
+    if ( lowTwo ) // not 4 byte aligned?
+#endif /*morkZone_CONFIG_ALIGN_8*/
+      ev->NewWarning("mZone_At not aligned");
+#endif /*morkZone_CONFIG_DEBUG*/
+  }
+  else // need to allocate a brand new run
+  {
+    inNeededSize += 7; // allow for possible alignment padding
+    mork_size newSize = ( inNeededSize > morkZone_kNewHunkSize )?
+      inNeededSize : morkZone_kNewHunkSize;
+      
+    morkHunk* hunk = this->zone_new_hunk(ev, newSize);
+    if ( hunk )
+    {
+      morkRun* hunkRun = hunk->HunkRun();
+      mork_u1* at = (mork_u1*) hunkRun->RunAsBlock();
+      mork_ip lowBits = ((mork_ip) at) & 7;
+      if ( lowBits ) // not 8 byte aligned?
+      {
+        mork_ip skip = (8 - lowBits); // skip the complement to align
+        at += skip;
+        newSize -= skip;
+      }
+      mZone_At = at;
+      mZone_AtSize = newSize;
+    }
+  }
+  
+  return mZone_AtSize;
+}
+
+morkHunk* morkZone::zone_new_hunk(morkEnv* ev, mdb_size inSize) // alloc  
+{
+  mdb_size hunkSize = inSize + sizeof(morkHunk);
+  void* outBlock = 0; // we are going straight to the heap:
+  mZone_Heap->Alloc(ev->AsMdbEnv(), hunkSize, &outBlock);
+  if ( outBlock )
+  {
+#ifdef morkZone_CONFIG_VOL_STATS
+    mZone_HeapVolume += hunkSize; // track all heap allocations
+#endif /* morkZone_CONFIG_VOL_STATS */
+  
+    morkHunk* hunk = (morkHunk*) outBlock;
+#ifdef morkHunk_USE_TAG_SLOT
+    hunk->HunkInitTag();
+#endif /* morkHunk_USE_TAG_SLOT */
+  
+    hunk->HunkSetNext(mZone_HunkList);
+    mZone_HunkList = hunk;
+    ++mZone_HunkCount;
+    
+    morkRun* run = hunk->HunkRun();
+    run->RunSetSize(inSize);
+#ifdef morkRun_USE_TAG_SLOT
+    run->RunInitTag();
+#endif /* morkRun_USE_TAG_SLOT */
+    
+    return hunk;
+  }
+  if ( ev->Good() ) // got this far without any error reported yet?
+    ev->OutOfMemoryError();
+  return (morkHunk*) 0;
+}
+
+void* morkZone::zone_new_chip(morkEnv* ev, mdb_size inSize) // alloc  
+{
+#ifdef morkZone_CONFIG_VOL_STATS
+  mZone_BlockVolume += inSize; // sum sizes of both chips and runs
+#endif /* morkZone_CONFIG_VOL_STATS */
+  
+  mork_u1* at = mZone_At;
+  mork_size atSize = mZone_AtSize; // available bytes in current hunk
+  if ( atSize >= inSize ) // current hunk can satisfy request?
+  {
+    mZone_At = at + inSize;
+    mZone_AtSize = atSize - inSize;
+    return at;
+  }
+  else if ( atSize > morkZone_kMaxHunkWaste ) // over max waste allowed?
+  {
+    morkHunk* hunk = this->zone_new_hunk(ev, inSize);
+    if ( hunk )
+      return hunk->HunkRun();
+      
+    return (void*) 0; // show allocation has failed
+  }
+  else // get ourselves a new hunk for suballocation:
+  {
+    atSize = this->zone_grow_at(ev, inSize); // get a new hunk
+  }
+
+  if ( atSize >= inSize ) // current hunk can satisfy request?
+  {
+    at = mZone_At;
+    mZone_At = at + inSize;
+    mZone_AtSize = atSize - inSize;
+    return at;
+  }
+  
+  if ( ev->Good() ) // got this far without any error reported yet?
+    ev->OutOfMemoryError();
+    
+  return (void*) 0; // show allocation has failed
+}
+
+void* morkZone::ZoneNewChip(morkEnv* ev, mdb_size inSize) // alloc  
+{
+#ifdef morkZone_CONFIG_ARENA
+
+#ifdef morkZone_CONFIG_DEBUG
+  if ( !this->IsZone() )
+    this->NonZoneTypeError(ev);
+  else if ( !mZone_Heap )
+    this->NilZoneHeapError(ev);
+#endif /*morkZone_CONFIG_DEBUG*/
+
+#ifdef morkZone_CONFIG_ALIGN_8
+  inSize += 7;
+  inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes
+#else /*morkZone_CONFIG_ALIGN_8*/
+  inSize += 3;
+  inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes
+#endif /*morkZone_CONFIG_ALIGN_8*/
+
+#ifdef morkZone_CONFIG_VOL_STATS
+  mZone_ChipVolume += inSize; // sum sizes of chips only
+#endif /* morkZone_CONFIG_VOL_STATS */
+
+  return this->zone_new_chip(ev, inSize);
+
+#else /*morkZone_CONFIG_ARENA*/
+  void* outBlock = 0;
+  mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock);
+  return outBlock;
+#endif /*morkZone_CONFIG_ARENA*/
+  
+}
+  
+// public: // ...but runs do indeed know how big they are
+void* morkZone::ZoneNewRun(morkEnv* ev, mdb_size inSize) // alloc  
+{
+#ifdef morkZone_CONFIG_ARENA
+
+#ifdef morkZone_CONFIG_DEBUG
+  if ( !this->IsZone() )
+    this->NonZoneTypeError(ev);
+  else if ( !mZone_Heap )
+    this->NilZoneHeapError(ev);
+#endif /*morkZone_CONFIG_DEBUG*/
+
+  inSize += morkZone_kRoundAdd;
+  inSize &= morkZone_kRoundMask;
+  if ( inSize <= morkZone_kMaxCachedRun )
+  {
+    morkRun** bucket = mZone_FreeRuns + (inSize >> morkZone_kRoundBits);
+    morkRun* hit = *bucket;
+    if ( hit ) // cache hit?
+    {
+      *bucket = hit->RunNext();
+      hit->RunSetSize(inSize);
+      return hit->RunAsBlock();
+    }
+  }
+  mdb_size blockSize = inSize + sizeof(morkRun); // plus run overhead
+#ifdef morkZone_CONFIG_VOL_STATS
+  mZone_RunVolume += blockSize; // sum sizes of runs only
+#endif /* morkZone_CONFIG_VOL_STATS */
+  morkRun* run = (morkRun*) this->zone_new_chip(ev, blockSize);
+  if ( run )
+  {
+    run->RunSetSize(inSize);
+#ifdef morkRun_USE_TAG_SLOT
+    run->RunInitTag();
+#endif /* morkRun_USE_TAG_SLOT */
+    return run->RunAsBlock();
+  }
+  
+  if ( ev->Good() ) // got this far without any error reported yet?
+    ev->OutOfMemoryError();
+  
+  return (void*) 0; // indicate failed allocation
+
+#else /*morkZone_CONFIG_ARENA*/
+  void* outBlock = 0;
+  mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock);
+  return outBlock;
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+void morkZone::ZoneZapRun(morkEnv* ev, void* ioRunBlock) // free
+{
+#ifdef morkZone_CONFIG_ARENA
+
+  morkRun* run = morkRun::BlockAsRun(ioRunBlock);
+  mdb_size runSize = run->RunSize();
+#ifdef morkZone_CONFIG_VOL_STATS
+  mZone_BlockVolume -= runSize; // tracking sizes of both chips and runs
+#endif /* morkZone_CONFIG_VOL_STATS */
+
+#ifdef morkZone_CONFIG_DEBUG
+  if ( !this->IsZone() )
+    this->NonZoneTypeError(ev);
+  else if ( !mZone_Heap )
+    this->NilZoneHeapError(ev);
+  else if ( !ioRunBlock )
+    ev->NilPointerError();
+  else if ( runSize & morkZone_kRoundAdd )
+    run->RunSizeAlignError(ev);
+#ifdef morkRun_USE_TAG_SLOT
+  else if ( !run->RunGoodTag() )
+    run->BadRunTagError(ev);
+#endif /* morkRun_USE_TAG_SLOT */
+#endif /*morkZone_CONFIG_DEBUG*/
+
+  if ( runSize <= morkZone_kMaxCachedRun ) // goes into free run list?
+  {
+    morkRun** bucket = mZone_FreeRuns + (runSize >> morkZone_kRoundBits);
+    run->RunSetNext(*bucket); // push onto free run list
+    *bucket = run;
+  }
+  else // free old run list
+  {
+    run->RunSetNext(mZone_FreeOldRunList); // push onto free old run list
+    mZone_FreeOldRunList = run;
+    ++mZone_FreeOldRunCount;
+#ifdef morkZone_CONFIG_VOL_STATS
+    mZone_FreeOldRunVolume += runSize;
+#endif /* morkZone_CONFIG_VOL_STATS */
+
+    morkOldRun* oldRun = (morkOldRun*) run; // to access extra size slot
+    oldRun->OldSetSize(runSize); // so we know how big this is later
+  }
+
+#else /*morkZone_CONFIG_ARENA*/
+  mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock);
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+void* morkZone::ZoneGrowRun(morkEnv* ev, void* ioRunBlock, mdb_size inSize)
+{
+#ifdef morkZone_CONFIG_ARENA
+
+  morkRun* run = morkRun::BlockAsRun(ioRunBlock);
+  mdb_size runSize = run->RunSize();
+
+#ifdef morkZone_CONFIG_DEBUG
+  if ( !this->IsZone() )
+    this->NonZoneTypeError(ev);
+  else if ( !mZone_Heap )
+    this->NilZoneHeapError(ev);
+#endif /*morkZone_CONFIG_DEBUG*/
+
+#ifdef morkZone_CONFIG_ALIGN_8
+  inSize += 7;
+  inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes
+#else /*morkZone_CONFIG_ALIGN_8*/
+  inSize += 3;
+  inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes
+#endif /*morkZone_CONFIG_ALIGN_8*/
+
+  if ( inSize > runSize )
+  {
+    void* newBuf = this->ZoneNewRun(ev, inSize);
+    if ( newBuf )
+    {
+      MORK_MEMCPY(newBuf, ioRunBlock, runSize);
+      this->ZoneZapRun(ev, ioRunBlock);
+      
+      return newBuf;
+    }
+  }
+  else
+    return ioRunBlock; // old size is big enough
+  
+  if ( ev->Good() ) // got this far without any error reported yet?
+    ev->OutOfMemoryError();
+  
+  return (void*) 0; // indicate failed allocation
+
+#else /*morkZone_CONFIG_ARENA*/
+  void* outBlock = 0;
+  mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock);
+  return outBlock;
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// { ===== begin nsIMdbHeap methods =====
+/*virtual*/ mdb_err
+morkZone::Alloc(nsIMdbEnv* mev, // allocate a piece of memory
+  mdb_size inSize,   // requested size of new memory block 
+  void** outBlock)  // memory block of inSize bytes, or nil
+{
+  mdb_err outErr = 0;
+  void* block = 0;
+  morkEnv* ev = morkEnv::FromMdbEnv(mev);
+  if ( ev )
+  {
+    block = this->ZoneNewRun(ev, inSize);
+    outErr = ev->AsErr();
+  }
+  else
+    outErr = 1;
+    
+  if ( outBlock )
+    *outBlock = block;
+    
+  return outErr;
+}
+  
+/*virtual*/ mdb_err
+morkZone::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc()
+  void* inBlock)
+{
+  mdb_err outErr = 0;
+  if ( inBlock )
+  {
+    morkEnv* ev = morkEnv::FromMdbEnv(mev);
+    if ( ev )
+    {
+      this->ZoneZapRun(ev, inBlock);
+      outErr = ev->AsErr();
+    }
+    else
+      outErr = 1;
+  }
+    
+  return outErr;
+}
+
+/*virtual*/ mdb_err
+morkZone::HeapAddStrongRef(nsIMdbEnv* mev) // does nothing
+{
+  MORK_USED_1(mev);
+  return 0;
+}
+
+/*virtual*/ mdb_err
+morkZone::HeapCutStrongRef(nsIMdbEnv* mev) // does nothing
+{
+  MORK_USED_1(mev);
+  return 0;
+}
+
+// } ===== end nsIMdbHeap methods =====
+
new file mode 100644
--- /dev/null
+++ b/db/mork/src/morkZone.h
@@ -0,0 +1,354 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKZONE_
+#define _MORKZONE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| CONFIG_DEBUG: do paranoid debug checks if defined.
+|*/
+#ifdef MORK_DEBUG
+#define morkZone_CONFIG_DEBUG 1 /* debug paranoid if defined */
+#endif /*MORK_DEBUG*/
+
+/*| CONFIG_STATS: keep volume and usage statistics.
+|*/
+#define morkZone_CONFIG_VOL_STATS 1 /* count space used by zone instance */
+
+/*| CONFIG_ARENA: if this is defined, then the morkZone class will alloc big
+**| blocks from the zone's heap, and suballocate from these.  If undefined,
+**| then morkZone will just pass all calls through to the zone's heap.
+|*/
+#ifdef MORK_ENABLE_ZONE_ARENAS
+#define morkZone_CONFIG_ARENA 1 /* be arena, if defined; otherwise no-op */
+#endif /*MORK_ENABLE_ZONE_ARENAS*/
+
+/*| CONFIG_ALIGN_8: if this is defined, then the morkZone class will give
+**| blocks 8 byte alignment instead of only 4 byte alignment.
+|*/
+#ifdef MORK_CONFIG_ALIGN_8
+#define morkZone_CONFIG_ALIGN_8 1 /* ifdef: align to 8 bytes, otherwise 4 */
+#endif /*MORK_CONFIG_ALIGN_8*/
+
+/*| CONFIG_PTR_SIZE_4: if this is defined, then the morkZone class will
+**| assume sizeof(void*) == 4, so a tag slot for padding is needed.
+|*/
+#ifdef MORK_CONFIG_PTR_SIZE_4
+#define morkZone_CONFIG_PTR_SIZE_4 1 /* ifdef: sizeof(void*) == 4 */
+#endif /*MORK_CONFIG_PTR_SIZE_4*/
+
+/*| morkZone_USE_TAG_SLOT: if this is defined, then define slot mRun_Tag
+**| in order to achieve eight byte alignment after the mRun_Next slot.
+|*/
+#if defined(morkZone_CONFIG_ALIGN_8) && defined(morkZone_CONFIG_PTR_SIZE_4)
+#define morkRun_USE_TAG_SLOT 1  /* need mRun_Tag slot inside morkRun */
+#define morkHunk_USE_TAG_SLOT 1 /* need mHunk_Tag slot inside morkHunk */
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkRun_kTag ((mork_u4) 0x6D52754E ) /* ascii 'mRuN' */
+
+/*| morkRun: structure used by morkZone for sized blocks
+|*/
+class morkRun {
+
+protected: // member variable slots
+#ifdef morkRun_USE_TAG_SLOT
+  mork_u4   mRun_Tag; // force 8 byte alignment after mRun_Next
+#endif /* morkRun_USE_TAG_SLOT */
+
+  morkRun*  mRun_Next;
+  
+public: // pointer interpretation of mRun_Next (when inside a list):
+  morkRun*   RunNext() const { return mRun_Next; }
+  void       RunSetNext(morkRun* ioNext) { mRun_Next = ioNext; }
+  
+public: // size interpretation of mRun_Next (when not inside a list):
+  mork_size  RunSize() const { return (mork_size) ((mork_ip) mRun_Next); }
+  void       RunSetSize(mork_size inSize)
+             { mRun_Next = (morkRun*) ((mork_ip) inSize); }
+  
+public: // maintenance and testing of optional tag magic signature slot:
+#ifdef morkRun_USE_TAG_SLOT
+  void       RunInitTag() { mRun_Tag = morkRun_kTag; }
+  mork_bool  RunGoodTag() { return ( mRun_Tag == morkRun_kTag ); }
+#endif /* morkRun_USE_TAG_SLOT */
+  
+public: // conversion back and forth to inline block following run instance:
+  void* RunAsBlock() { return (((mork_u1*) this) + sizeof(morkRun)); }
+  
+  static morkRun* BlockAsRun(void* ioBlock)
+  { return (morkRun*) (((mork_u1*) ioBlock) - sizeof(morkRun)); }
+
+public: // typing & errors
+  static void BadRunTagError(morkEnv* ev);
+  static void RunSizeAlignError(morkEnv* ev);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+/*| morkOldRun: more space to record size when run is put into old free list
+|*/
+class morkOldRun : public morkRun {
+
+protected: // need another size field when mRun_Next is used for linkage:
+  mdb_size mOldRun_Size;
+  
+public: // size getter/setter
+  mork_size  OldSize() const { return mOldRun_Size; }
+  void       OldSetSize(mork_size inSize) { mOldRun_Size = inSize; }
+  
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkHunk_kTag ((mork_u4) 0x68556E4B ) /* ascii 'hUnK' */
+
+/*| morkHunk: structure used by morkZone for heap allocations.
+|*/
+class morkHunk {
+
+protected: // member variable slots
+
+#ifdef morkHunk_USE_TAG_SLOT
+  mork_u4   mHunk_Tag; // force 8 byte alignment after mHunk_Next
+#endif /* morkHunk_USE_TAG_SLOT */
+
+  morkHunk* mHunk_Next;
+  
+  morkRun   mHunk_Run;
+  
+public: // setters
+  void      HunkSetNext(morkHunk* ioNext) { mHunk_Next = ioNext; }
+  
+public: // getters
+  morkHunk* HunkNext() const { return mHunk_Next; }
+
+  morkRun*  HunkRun() { return &mHunk_Run; }
+  
+public: // maintenance and testing of optional tag magic signature slot:
+#ifdef morkHunk_USE_TAG_SLOT
+  void       HunkInitTag() { mHunk_Tag = morkHunk_kTag; }
+  mork_bool  HunkGoodTag() { return ( mHunk_Tag == morkHunk_kTag ); }
+#endif /* morkHunk_USE_TAG_SLOT */
+
+public: // typing & errors
+  static void BadHunkTagWarning(morkEnv* ev);
+  
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| kNewHunkSize: the default size for a hunk, assuming we must allocate
+**| a new one whenever the free hunk list does not already have.  Note this
+**| number should not be changed without also considering suitable changes
+**| in the related kMaxHunkWaste and kMinHunkSize constants. 
+|*/
+#define morkZone_kNewHunkSize ((mork_size) (64 * 1024)) /* 64K per hunk */
+
+/*| kMaxFreeVolume: some number of bytes of free space in the free hunk list
+**| over which we no longer want to add more free hunks to the list, for fear
+**| of accumulating too much unused, fragmented free space.  This should be a
+**| small multiple of kNewHunkSize, say about two to four times as great, to
+**| allow for no more free hunk space than fits in a handful of new hunks.
+**| This strategy will let us usefuly accumulate "some" free space in the
+**| free hunk list, but without accumulating "too much" free space that way.
+|*/
+#define morkZone_kMaxFreeVolume (morkZone_kNewHunkSize * 3)
+
+/*| kMaxHunkWaste: if a current request is larger than this, and we cannot
+**| satisfy the request with the current hunk, then we just allocate the
+**| block from the heap without changing the current hunk.  Basically this
+**| number represents the largest amount of memory we are willing to waste,
+**| since a block request barely less than this can cause the current hunk
+**| to be retired (with any unused space wasted) as well get a new hunk.
+|*/
+#define morkZone_kMaxHunkWaste ((mork_size) 4096) /* 1/16 kNewHunkSize */
+
+/*| kRound*: the algorithm for rounding up allocation sizes for caching
+**| in free lists works like the following.  We add kRoundAdd to any size
+**| requested, and then bitwise AND with kRoundMask, and this will give us
+**| the smallest multiple of kRoundSize that is at least as large as the
+**| requested size.  Then if we rightshift this number by kRoundBits, we
+**| will have the index into the mZone_FreeRuns array which will hold any
+**| cache runs of that size.  So 4 bits of shift gives us a granularity
+**| of 16 bytes, so that free lists will hold successive runs that are
+**| 16 bytes greater than the next smaller run size.  If we have 256 free
+**| lists of nonzero sized runs altogether, then the largest run that can
+**| be cached is 4096, or 4K (since 4096 == 16 * 256).  A larger run that
+**| gets freed will go in to the free hunk list (or back to the heap).
+|*/
+#define morkZone_kRoundBits 4 /* bits to round-up size for free lists */
+#define morkZone_kRoundSize (1 << morkZone_kRoundBits)
+#define morkZone_kRoundAdd ((1 << morkZone_kRoundBits) - 1)
+#define morkZone_kRoundMask (~ ((mork_ip) morkZone_kRoundAdd))
+
+#define morkZone_kBuckets 256 /* number of distinct free lists */
+
+/*| kMaxCachedRun: the largest run that will be stored inside a free
+**| list of old zapped runs.  A run larger than this cannot be put in
+**| a free list, and must be allocated from the heap at need, and put
+**| into the free hunk list when discarded.
+|*/
+#define morkZone_kMaxCachedRun (morkZone_kBuckets * morkZone_kRoundSize)
+
+#define morkDerived_kZone     /*i*/ 0x5A6E /* ascii 'Zn' */
+
+/*| morkZone: a pooled memory allocator like an NSPR arena.  The term 'zone'
+**| is roughly synonymous with 'heap'.  I avoid calling this class a "heap"
+**| to avoid any confusion with nsIMdbHeap, and I avoid calling this class
+**| an arean to avoid confusion with NSPR usage.
+|*/
+class morkZone : public morkNode, public nsIMdbHeap {
+
+// public: // slots inherited from morkNode (meant to inform only)
+  // nsIMdbHeap*       mNode_Heap;
+
+  // mork_base      mNode_Base;     // must equal morkBase_kNode
+  // mork_derived   mNode_Derived;  // depends on specific node subclass
+  
+  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
+  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
+  // mork_able      mNode_Mutable;  // can this node be modified?
+  // mork_load      mNode_Load;     // is this node clean or dirty?
+  
+  // mork_uses      mNode_Uses;     // refcount for strong refs
+  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+  nsIMdbHeap*  mZone_Heap; // strong ref to heap allocating all space
+  
+  mork_size    mZone_HeapVolume;  // total bytes allocated from heap
+  mork_size    mZone_BlockVolume; // total bytes in all zone blocks
+  mork_size    mZone_RunVolume;   // total bytes in all zone runs
+  mork_size    mZone_ChipVolume;  // total bytes in all zone chips
+  
+  mork_size    mZone_FreeOldRunVolume; // total bytes in all used hunks
+  
+  mork_count   mZone_HunkCount;        // total number of used hunks
+  mork_count   mZone_FreeOldRunCount;  // total free old runs
+
+  morkHunk*    mZone_HunkList;       // linked list of all used hunks  
+  morkRun*     mZone_FreeOldRunList; // linked list of free old runs
+  
+  // note mZone_At is a byte pointer for single byte address arithmetic:
+  mork_u1*     mZone_At;     // current position in most recent hunk
+  mork_size    mZone_AtSize; // number of bytes remaining in this hunk
+  
+  // kBuckets+1 so indexes zero through kBuckets are all okay to use:
+  
+  morkRun*     mZone_FreeRuns[ morkZone_kBuckets + 1 ];
+  // Each piece of memory stored in list mZone_FreeRuns[ i ] has an
+  // allocation size equal to sizeof(morkRun) + (i * kRoundSize), so
+  // that callers can be given a piece of memory with (i * kRoundSize)
+  // bytes of writeable space while reserving the first sizeof(morkRun)
+  // bytes to keep track of size information for later re-use.  Note
+  // that mZone_FreeRuns[ 0 ] is unused because no run will be zero
+  // bytes in size (and morkZone plans to complain about zero sizes). 
+
+protected: // zone utilities
+  
+  mork_size zone_grow_at(morkEnv* ev, mork_size inNeededSize);
+
+  void*     zone_new_chip(morkEnv* ev, mdb_size inSize); // alloc  
+  morkHunk* zone_new_hunk(morkEnv* ev, mdb_size inRunSize); // alloc  
+
+// { ===== begin nsIMdbHeap methods =====
+public:
+  NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory
+    mdb_size inSize,   // requested size of new memory block 
+    void** outBlock);  // memory block of inSize bytes, or nil
+    
+  NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc()
+    void* inBlock);
+    
+  NS_IMETHOD HeapAddStrongRef(nsIMdbEnv* ev); // does nothing
+  NS_IMETHOD HeapCutStrongRef(nsIMdbEnv* ev); // does nothing
+// } ===== end nsIMdbHeap methods =====
+  
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+  virtual void CloseMorkNode(morkEnv* ev); // CloseZone() only if open
+  virtual ~morkZone(); // assert that CloseMap() executed earlier
+  
+public: // morkMap construction & destruction
+  morkZone(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, 
+    nsIMdbHeap* ioZoneHeap);
+  
+  void CloseZone(morkEnv* ev); // called by CloseMorkNode()
+  
+public: // dynamic type identification
+  mork_bool IsZone() const
+  { return IsNode() && mNode_Derived == morkDerived_kZone; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkZone methods =====
+public: // chips do not know how big they are...
+  void* ZoneNewChip(morkEnv* ev, mdb_size inSize); // alloc  
+    
+public: // ...but runs do indeed know how big they are
+  void* ZoneNewRun(morkEnv* ev, mdb_size inSize); // alloc  
+  void  ZoneZapRun(morkEnv* ev, void* ioRunBody); // free
+  void* ZoneGrowRun(morkEnv* ev, void* ioRunBody, mdb_size inSize); // realloc
+    
+// } ===== end morkZone methods =====
+
+public: // typing & errors
+  static void NonZoneTypeError(morkEnv* ev);
+  static void NilZoneHeapError(morkEnv* ev);
+  static void BadZoneTagError(morkEnv* ev);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKZONE_ */
new file mode 100644
--- /dev/null
+++ b/db/mork/src/orkinHeap.cpp
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _ORKINHEAP_
+#include "orkinHeap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#include <stdlib.h>
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+orkinHeap::orkinHeap() // does nothing
+#ifdef MORK_DEBUG_HEAP_STATS
+  : sHeap_AllocCount( 0 )
+  , sHeap_FreeCount( 0 )
+  , sHeap_BlockCount( 0 )
+  
+  , sHeap_BlockVolume( 0 )
+  , sHeap_HighWaterVolume( 0 )
+  , sHeap_HighWaterTenKilo( 0 )
+  , sHeap_HighWaterHundredKilo( 0 )
+#endif /*MORK_DEBUG_HEAP_STATS*/
+{
+}
+
+/*virtual*/
+orkinHeap::~orkinHeap() // does nothing
+{
+}
+
+// { ===== begin nsIMdbHeap methods =====
+/*virtual*/ mdb_err
+orkinHeap::Alloc(nsIMdbEnv* mev, // allocate a piece of memory
+  mdb_size inSize,   // requested size of new memory block 
+  void** outBlock)  // memory block of inSize bytes, or nil
+{
+#ifdef MORK_DEBUG_HEAP_STATS
+  mdb_size realSize = inSize;
+  inSize += 12; // sizeof(mork_u4) * 3
+  ++sHeap_AllocCount;
+#endif /*MORK_DEBUG_HEAP_STATS*/
+
+  MORK_USED_1(mev);
+  mdb_err outErr = 0;
+  void* block = malloc(inSize);
+  if ( !block )
+    outErr = morkEnv_kOutOfMemoryError;
+#ifdef MORK_DEBUG_HEAP_STATS
+  else
+  {
+    printf("%lx allocating %d\n", this, realSize);
+    mork_u4* array = (mork_u4*) block;
+    *array++ = (mork_u4) this;
+    *array++ = realSize;
+    *array++ = orkinHeap_kTag;
+    block = array;
+    ++sHeap_BlockCount;
+    mork_num blockVol = sHeap_BlockVolume + realSize;
+    sHeap_BlockVolume = blockVol;
+    if ( blockVol > sHeap_HighWaterVolume )
+    {
+      sHeap_HighWaterVolume = blockVol;
+      
+      mork_num tenKiloVol = blockVol / (10 * 1024);
+      if ( tenKiloVol > sHeap_HighWaterTenKilo )
+      {
+        sHeap_HighWaterTenKilo = tenKiloVol;
+      
+        mork_num hundredKiloVol = blockVol / (100 * 1024);
+        if ( hundredKiloVol > sHeap_HighWaterHundredKilo )
+          sHeap_HighWaterHundredKilo = hundredKiloVol;
+      }
+    }
+  }
+#endif /*MORK_DEBUG_HEAP_STATS*/
+    
+  MORK_ASSERT(outBlock);
+  if ( outBlock )
+    *outBlock = block;
+  return outErr;
+}
+  
+/*virtual*/ mdb_err
+orkinHeap::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc()
+  void* inBlock)
+{
+#ifdef MORK_DEBUG_HEAP_STATS
+  ++sHeap_FreeCount;
+#endif /*MORK_DEBUG_HEAP_STATS*/
+
+  MORK_USED_1(mev);
+  MORK_ASSERT(inBlock);
+  if ( inBlock )
+  {
+#ifdef MORK_DEBUG_HEAP_STATS
+    morkEnv* ev = 0; //morkEnv::FromMdbEnv(mev);
+    mork_u4* array = (mork_u4*) inBlock;
+    if ( *--array != orkinHeap_kTag )
+    {
+      if ( ev )
+        ev->NewWarning("heap block tag not hEaP");
+    }
+    mork_u4 realSize = *--array;
+    inBlock = --array; // skip over heap ptr too.
+    
+    printf("%lx freeing %d\n", this, realSize);
+    if ( sHeap_BlockCount )
+      --sHeap_BlockCount;
+    else if ( ev ) 
+      ev->NewWarning("sHeap_BlockCount underflow");
+    
+    if ( sHeap_BlockVolume >= realSize )
+      sHeap_BlockVolume -= realSize;
+    else if ( ev )
+    {
+      sHeap_BlockVolume = 0;
+      ev->NewWarning("sHeap_BlockVolume underflow");
+    }
+#endif /*MORK_DEBUG_HEAP_STATS*/
+    
+    free(inBlock);
+  }
+  return 0;
+}
+
+/*virtual*/ mdb_err
+orkinHeap::HeapAddStrongRef(nsIMdbEnv* ev) // does nothing
+{
+  MORK_USED_1(ev);
+  return 0;
+}
+
+/*virtual*/ mdb_err
+orkinHeap::HeapCutStrongRef(nsIMdbEnv* ev) // does nothing
+{
+  MORK_USED_1(ev);
+  return 0;
+}
+
+// } ===== end nsIMdbHeap methods =====
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
new file mode 100644
--- /dev/null
+++ b/db/mork/src/orkinHeap.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _ORKINHEAP_
+#define _ORKINHEAP_ 1
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define orkinHeap_kTag 0x68456150 /* ascii 'hEaP' */
+
+/*| orkinHeap: 
+|*/
+class orkinHeap : public nsIMdbHeap { //
+
+#ifdef MORK_DEBUG_HEAP_STATS
+protected:
+  mork_num sHeap_AllocCount;  // number of times Alloc() is called
+  mork_num sHeap_FreeCount;   // number of times Free() is called
+  mork_num sHeap_BlockCount;  // number of outstanding blocks
+  
+  mork_num sHeap_BlockVolume; // sum of sizes for all outstanding blocks
+  mork_num sHeap_HighWaterVolume;  // largest value of sHeap_BlockVolume seen
+  mork_num sHeap_HighWaterTenKilo; // HighWaterVolume in 10K granularity
+  mork_num sHeap_HighWaterHundredKilo; // HighWaterVolume in 100K granularity
+  
+public: // getters
+  mork_num HeapAllocCount() const { return sHeap_AllocCount; }
+  mork_num HeapFreeCount() const { return sHeap_FreeCount; }
+  mork_num HeapBlockCount() const { return sHeap_AllocCount - sHeap_FreeCount; }
+  
+  mork_num HeapBlockVolume() const { return sHeap_BlockVolume; }
+  mork_num HeapHighWaterVolume() const { return sHeap_HighWaterVolume; }
+#endif /*MORK_DEBUG_HEAP_STATS*/
+  
+public:
+  orkinHeap(); // does nothing
+  virtual ~orkinHeap(); // does nothing
+    
+private: // copying is not allowed
+  orkinHeap(const orkinHeap& other);
+  orkinHeap& operator=(const orkinHeap& other);
+
+public:
+
+// { ===== begin nsIMdbHeap methods =====
+  NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory
+    mdb_size inSize,   // requested size of new memory block 
+    void** outBlock);  // memory block of inSize bytes, or nil
+    
+  NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc()
+    void* inBlock);
+    
+  NS_IMETHOD HeapAddStrongRef(nsIMdbEnv* ev); // does nothing
+  NS_IMETHOD HeapCutStrongRef(nsIMdbEnv* ev); // does nothing
+// } ===== end nsIMdbHeap methods =====
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _ORKINHEAP_ */